feat(napi): extends the Map types interoperability (#1950)

This commit is contained in:
LongYinan 2024-02-15 15:43:21 +08:00 committed by GitHub
parent 642cecc759
commit d7dc4dc5a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 175 additions and 2 deletions

View file

@ -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"

View file

@ -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)
}
}

View file

@ -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",

View file

@ -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␊

View file

@ -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) => {

View file

@ -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

View file

@ -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
}