feat(napi): extends the Map types interoperability (#1950)
This commit is contained in:
parent
642cecc759
commit
d7dc4dc5a2
8 changed files with 175 additions and 2 deletions
|
@ -28,6 +28,7 @@ deferred_trace = ["napi4"]
|
||||||
error_anyhow = ["anyhow"]
|
error_anyhow = ["anyhow"]
|
||||||
experimental = ["napi-sys/experimental"]
|
experimental = ["napi-sys/experimental"]
|
||||||
full = ["latin1", "napi9", "async", "serde-json", "experimental", "chrono_date"]
|
full = ["latin1", "napi9", "async", "serde-json", "experimental", "chrono_date"]
|
||||||
|
object_indexmap = ["indexmap"]
|
||||||
latin1 = ["encoding_rs"]
|
latin1 = ["encoding_rs"]
|
||||||
napi1 = []
|
napi1 = []
|
||||||
napi2 = ["napi1", "napi-sys/napi2"]
|
napi2 = ["napi1", "napi-sys/napi2"]
|
||||||
|
@ -40,6 +41,7 @@ napi8 = ["napi7", "napi-sys/napi8"]
|
||||||
napi9 = ["napi8", "napi-sys/napi9"]
|
napi9 = ["napi8", "napi-sys/napi9"]
|
||||||
noop = []
|
noop = []
|
||||||
serde-json = ["serde", "serde_json"]
|
serde-json = ["serde", "serde_json"]
|
||||||
|
serde-json-ordered = ["serde-json", "serde_json/preserve_order"]
|
||||||
tokio_fs = ["tokio/fs"]
|
tokio_fs = ["tokio/fs"]
|
||||||
tokio_full = ["tokio/full"]
|
tokio_full = ["tokio/full"]
|
||||||
tokio_io_std = ["tokio/io-std"]
|
tokio_io_std = ["tokio/io-std"]
|
||||||
|
@ -93,3 +95,7 @@ version = "1"
|
||||||
[dependencies.serde_json]
|
[dependencies.serde_json]
|
||||||
optional = true
|
optional = true
|
||||||
version = "1"
|
version = "1"
|
||||||
|
|
||||||
|
[dependencies.indexmap]
|
||||||
|
optional = true
|
||||||
|
version = "2"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::hash::{BuildHasher, Hash};
|
use std::hash::{BuildHasher, Hash};
|
||||||
|
|
||||||
|
#[cfg(feature = "object_indexmap")]
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
use crate::bindgen_prelude::{Env, Result, ToNapiValue, *};
|
use crate::bindgen_prelude::{Env, Result, ToNapiValue, *};
|
||||||
|
|
||||||
impl<K, V, S> TypeName for HashMap<K, V, S> {
|
impl<K, V, S> TypeName for HashMap<K, V, S> {
|
||||||
|
@ -49,3 +52,101 @@ where
|
||||||
Ok(map)
|
Ok(map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<K, V> TypeName for BTreeMap<K, V> {
|
||||||
|
fn type_name() -> &'static str {
|
||||||
|
"BTreeMap"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value_type() -> ValueType {
|
||||||
|
ValueType::Object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: From<String> + Ord, V: FromNapiValue> ValidateNapiValue for BTreeMap<K, V> {}
|
||||||
|
|
||||||
|
impl<K, V> ToNapiValue for BTreeMap<K, V>
|
||||||
|
where
|
||||||
|
K: AsRef<str>,
|
||||||
|
V: ToNapiValue,
|
||||||
|
{
|
||||||
|
unsafe fn to_napi_value(raw_env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
|
||||||
|
let env = Env::from(raw_env);
|
||||||
|
let mut obj = env.create_object()?;
|
||||||
|
for (k, v) in val.into_iter() {
|
||||||
|
obj.set(k.as_ref(), v)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { Object::to_napi_value(raw_env, obj) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> FromNapiValue for BTreeMap<K, V>
|
||||||
|
where
|
||||||
|
K: From<String> + Ord,
|
||||||
|
V: FromNapiValue,
|
||||||
|
{
|
||||||
|
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
|
||||||
|
let obj = unsafe { Object::from_napi_value(env, napi_val)? };
|
||||||
|
let mut map = BTreeMap::default();
|
||||||
|
for key in Object::keys(&obj)?.into_iter() {
|
||||||
|
if let Some(val) = obj.get(&key)? {
|
||||||
|
map.insert(K::from(key), val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "object_indexmap")]
|
||||||
|
impl<K, V, S> TypeName for IndexMap<K, V, S> {
|
||||||
|
fn type_name() -> &'static str {
|
||||||
|
"IndexMap"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value_type() -> ValueType {
|
||||||
|
ValueType::Object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "object_indexmap")]
|
||||||
|
impl<K: From<String> + Hash + Eq, V: FromNapiValue> ValidateNapiValue for IndexMap<K, V> {}
|
||||||
|
|
||||||
|
#[cfg(feature = "object_indexmap")]
|
||||||
|
impl<K, V, S> ToNapiValue for IndexMap<K, V, S>
|
||||||
|
where
|
||||||
|
K: AsRef<str>,
|
||||||
|
V: ToNapiValue,
|
||||||
|
S: Default + BuildHasher,
|
||||||
|
{
|
||||||
|
unsafe fn to_napi_value(raw_env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
|
||||||
|
let env = Env::from(raw_env);
|
||||||
|
let mut obj = env.create_object()?;
|
||||||
|
for (k, v) in val.into_iter() {
|
||||||
|
obj.set(k.as_ref(), v)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { Object::to_napi_value(raw_env, obj) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "object_indexmap")]
|
||||||
|
impl<K, V, S> FromNapiValue for IndexMap<K, V, S>
|
||||||
|
where
|
||||||
|
K: From<String> + Hash + Eq,
|
||||||
|
V: FromNapiValue,
|
||||||
|
S: Default + BuildHasher,
|
||||||
|
{
|
||||||
|
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
|
||||||
|
let obj = unsafe { Object::from_napi_value(env, napi_val)? };
|
||||||
|
let mut map = IndexMap::default();
|
||||||
|
for key in Object::keys(&obj)?.into_iter() {
|
||||||
|
if let Some(val) = obj.get(&key)? {
|
||||||
|
map.insert(K::from(key), val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ napi-shared = { path = "../napi-shared" }
|
||||||
serde = "1"
|
serde = "1"
|
||||||
serde_derive = "1"
|
serde_derive = "1"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
indexmap = "2"
|
||||||
|
|
||||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||||
napi = { path = "../../crates/napi", default-features = false, features = [
|
napi = { path = "../../crates/napi", default-features = false, features = [
|
||||||
|
@ -27,6 +28,7 @@ napi = { path = "../../crates/napi", default-features = false, features = [
|
||||||
"experimental",
|
"experimental",
|
||||||
"latin1",
|
"latin1",
|
||||||
"chrono_date",
|
"chrono_date",
|
||||||
|
"object_indexmap",
|
||||||
"tokio",
|
"tokio",
|
||||||
"async",
|
"async",
|
||||||
"tokio_rt",
|
"tokio_rt",
|
||||||
|
@ -43,6 +45,7 @@ napi = { path = "../../crates/napi", default-features = false, features = [
|
||||||
"experimental",
|
"experimental",
|
||||||
"latin1",
|
"latin1",
|
||||||
"chrono_date",
|
"chrono_date",
|
||||||
|
"object_indexmap",
|
||||||
"tokio",
|
"tokio",
|
||||||
"async",
|
"async",
|
||||||
"tokio_rt",
|
"tokio_rt",
|
||||||
|
|
|
@ -392,6 +392,8 @@ Generated by [AVA](https://avajs.dev).
|
||||||
␊
|
␊
|
||||||
export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): void␊
|
export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): void␊
|
||||||
␊
|
␊
|
||||||
|
export function getBtreeMapping(): BTreeMap␊
|
||||||
|
␊
|
||||||
export function getBuffer(): Buffer␊
|
export function getBuffer(): Buffer␊
|
||||||
␊
|
␊
|
||||||
export function getCwd(callback: (arg0: string) => void): void␊
|
export function getCwd(callback: (arg0: string) => void): void␊
|
||||||
|
@ -402,6 +404,8 @@ Generated by [AVA](https://avajs.dev).
|
||||||
␊
|
␊
|
||||||
export function getGlobal(): typeof global␊
|
export function getGlobal(): typeof global␊
|
||||||
␊
|
␊
|
||||||
|
export function getIndexMapping(): IndexMap␊
|
||||||
|
␊
|
||||||
export function getMapping(): Record<string, number>␊
|
export function getMapping(): Record<string, number>␊
|
||||||
␊
|
␊
|
||||||
export function getModuleFileName(): string␊
|
export function getModuleFileName(): string␊
|
||||||
|
@ -425,6 +429,8 @@ Generated by [AVA](https://avajs.dev).
|
||||||
␊
|
␊
|
||||||
export function getWords(): Array<string>␊
|
export function getWords(): Array<string>␊
|
||||||
␊
|
␊
|
||||||
|
export function indexmapPassthrough(fixture: IndexMap): IndexMap␊
|
||||||
|
␊
|
||||||
/** default enum values are continuos i32s start from 0 */␊
|
/** default enum values are continuos i32s start from 0 */␊
|
||||||
export const enum Kind {␊
|
export const enum Kind {␊
|
||||||
/** Barks */␊
|
/** Barks */␊
|
||||||
|
@ -544,6 +550,10 @@ Generated by [AVA](https://avajs.dev).
|
||||||
name: string␊
|
name: string␊
|
||||||
}␊
|
}␊
|
||||||
␊
|
␊
|
||||||
|
export function sumBtreeMapping(nums: BTreeMap): number␊
|
||||||
|
␊
|
||||||
|
export function sumIndexMapping(nums: IndexMap): number␊
|
||||||
|
␊
|
||||||
export function sumMapping(nums: Record<string, number>): number␊
|
export function sumMapping(nums: Record<string, number>): number␊
|
||||||
␊
|
␊
|
||||||
export function sumNums(nums: Array<number>): number␊
|
export function sumNums(nums: Array<number>): number␊
|
||||||
|
|
Binary file not shown.
|
@ -34,6 +34,11 @@ const {
|
||||||
sumNums,
|
sumNums,
|
||||||
getMapping,
|
getMapping,
|
||||||
sumMapping,
|
sumMapping,
|
||||||
|
getBtreeMapping,
|
||||||
|
sumBtreeMapping,
|
||||||
|
getIndexMapping,
|
||||||
|
sumIndexMapping,
|
||||||
|
indexmapPassthrough,
|
||||||
getCwd,
|
getCwd,
|
||||||
Animal,
|
Animal,
|
||||||
Kind,
|
Kind,
|
||||||
|
@ -202,6 +207,11 @@ test('array', (t) => {
|
||||||
test('map', (t) => {
|
test('map', (t) => {
|
||||||
t.deepEqual(getMapping(), { a: 101, b: 102 })
|
t.deepEqual(getMapping(), { a: 101, b: 102 })
|
||||||
t.is(sumMapping({ a: 101, b: 102 }), 203)
|
t.is(sumMapping({ a: 101, b: 102 }), 203)
|
||||||
|
t.deepEqual(getBtreeMapping(), { a: 101, b: 102 })
|
||||||
|
t.is(sumBtreeMapping({ a: 101, b: 102 }), 203)
|
||||||
|
t.deepEqual(getIndexMapping(), { a: 101, b: 102 })
|
||||||
|
t.is(sumIndexMapping({ a: 101, b: 102 }), 203)
|
||||||
|
t.deepEqual(indexmapPassthrough({ a: 101, b: 102 }), { a: 101, b: 102 })
|
||||||
})
|
})
|
||||||
|
|
||||||
test('enum', (t) => {
|
test('enum', (t) => {
|
||||||
|
|
10
examples/napi/index.d.ts
vendored
10
examples/napi/index.d.ts
vendored
|
@ -382,6 +382,8 @@ export function fibonacci(n: number): number
|
||||||
|
|
||||||
export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): void
|
export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): void
|
||||||
|
|
||||||
|
export function getBtreeMapping(): BTreeMap
|
||||||
|
|
||||||
export function getBuffer(): Buffer
|
export function getBuffer(): Buffer
|
||||||
|
|
||||||
export function getCwd(callback: (arg0: string) => void): void
|
export function getCwd(callback: (arg0: string) => void): void
|
||||||
|
@ -392,6 +394,8 @@ export function getExternal(external: ExternalObject<number>): number
|
||||||
|
|
||||||
export function getGlobal(): typeof global
|
export function getGlobal(): typeof global
|
||||||
|
|
||||||
|
export function getIndexMapping(): IndexMap
|
||||||
|
|
||||||
export function getMapping(): Record<string, number>
|
export function getMapping(): Record<string, number>
|
||||||
|
|
||||||
export function getModuleFileName(): string
|
export function getModuleFileName(): string
|
||||||
|
@ -415,6 +419,8 @@ export function getUndefined(): void
|
||||||
|
|
||||||
export function getWords(): Array<string>
|
export function getWords(): Array<string>
|
||||||
|
|
||||||
|
export function indexmapPassthrough(fixture: IndexMap): IndexMap
|
||||||
|
|
||||||
/** default enum values are continuos i32s start from 0 */
|
/** default enum values are continuos i32s start from 0 */
|
||||||
export const enum Kind {
|
export const enum Kind {
|
||||||
/** Barks */
|
/** Barks */
|
||||||
|
@ -534,6 +540,10 @@ export interface StrictObject {
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function sumBtreeMapping(nums: BTreeMap): number
|
||||||
|
|
||||||
|
export function sumIndexMapping(nums: IndexMap): number
|
||||||
|
|
||||||
export function sumMapping(nums: Record<string, number>): number
|
export function sumMapping(nums: Record<string, number>): number
|
||||||
|
|
||||||
export function sumNums(nums: Array<number>): number
|
export function sumNums(nums: Array<number>): number
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
fn get_mapping() -> HashMap<String, u32> {
|
fn get_mapping() -> HashMap<String, u32> {
|
||||||
|
@ -12,3 +14,34 @@ fn get_mapping() -> HashMap<String, u32> {
|
||||||
fn sum_mapping(nums: HashMap<String, u32>) -> u32 {
|
fn sum_mapping(nums: HashMap<String, u32>) -> u32 {
|
||||||
nums.into_values().sum()
|
nums.into_values().sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
fn get_btree_mapping() -> BTreeMap<String, u32> {
|
||||||
|
let mut map = BTreeMap::new();
|
||||||
|
map.insert("a".to_string(), 101);
|
||||||
|
map.insert("b".to_string(), 102);
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
fn sum_btree_mapping(nums: BTreeMap<String, u32>) -> u32 {
|
||||||
|
nums.into_values().sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
fn get_index_mapping() -> IndexMap<String, u32> {
|
||||||
|
let mut map = IndexMap::new();
|
||||||
|
map.insert("a".to_string(), 101);
|
||||||
|
map.insert("b".to_string(), 102);
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
fn sum_index_mapping(nums: IndexMap<String, u32>) -> u32 {
|
||||||
|
nums.into_values().sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
fn indexmap_passthrough(fixture: IndexMap<String, u32>) -> IndexMap<String, u32> {
|
||||||
|
fixture
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue