diff --git a/crates/napi/Cargo.toml b/crates/napi/Cargo.toml index d1990c7f..22f864db 100644 --- a/crates/napi/Cargo.toml +++ b/crates/napi/Cargo.toml @@ -28,6 +28,7 @@ deferred_trace = ["napi4"] error_anyhow = ["anyhow"] experimental = ["napi-sys/experimental"] full = ["latin1", "napi9", "async", "serde-json", "experimental", "chrono_date"] +object_indexmap = ["indexmap"] latin1 = ["encoding_rs"] napi1 = [] napi2 = ["napi1", "napi-sys/napi2"] @@ -40,6 +41,7 @@ napi8 = ["napi7", "napi-sys/napi8"] napi9 = ["napi8", "napi-sys/napi9"] noop = [] serde-json = ["serde", "serde_json"] +serde-json-ordered = ["serde-json", "serde_json/preserve_order"] tokio_fs = ["tokio/fs"] tokio_full = ["tokio/full"] tokio_io_std = ["tokio/io-std"] @@ -93,3 +95,7 @@ version = "1" [dependencies.serde_json] optional = true version = "1" + +[dependencies.indexmap] +optional = true +version = "2" diff --git a/crates/napi/src/bindgen_runtime/js_values/map.rs b/crates/napi/src/bindgen_runtime/js_values/map.rs index 1a7e8c28..4534da0e 100644 --- a/crates/napi/src/bindgen_runtime/js_values/map.rs +++ b/crates/napi/src/bindgen_runtime/js_values/map.rs @@ -1,6 +1,9 @@ -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::hash::{BuildHasher, Hash}; +#[cfg(feature = "object_indexmap")] +use indexmap::IndexMap; + use crate::bindgen_prelude::{Env, Result, ToNapiValue, *}; impl TypeName for HashMap { @@ -49,3 +52,101 @@ where Ok(map) } } + +impl TypeName for BTreeMap { + fn type_name() -> &'static str { + "BTreeMap" + } + + fn value_type() -> ValueType { + ValueType::Object + } +} + +impl + Ord, V: FromNapiValue> ValidateNapiValue for BTreeMap {} + +impl ToNapiValue for BTreeMap +where + K: AsRef, + V: ToNapiValue, +{ + unsafe fn to_napi_value(raw_env: sys::napi_env, val: Self) -> Result { + 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 FromNapiValue for BTreeMap +where + K: From + Ord, + V: FromNapiValue, +{ + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + 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 TypeName for IndexMap { + fn type_name() -> &'static str { + "IndexMap" + } + + fn value_type() -> ValueType { + ValueType::Object + } +} + +#[cfg(feature = "object_indexmap")] +impl + Hash + Eq, V: FromNapiValue> ValidateNapiValue for IndexMap {} + +#[cfg(feature = "object_indexmap")] +impl ToNapiValue for IndexMap +where + K: AsRef, + V: ToNapiValue, + S: Default + BuildHasher, +{ + unsafe fn to_napi_value(raw_env: sys::napi_env, val: Self) -> Result { + 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 FromNapiValue for IndexMap +where + K: From + Hash + Eq, + V: FromNapiValue, + S: Default + BuildHasher, +{ + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + 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) + } +} diff --git a/examples/napi/Cargo.toml b/examples/napi/Cargo.toml index 311e49a5..2a359cf4 100644 --- a/examples/napi/Cargo.toml +++ b/examples/napi/Cargo.toml @@ -19,6 +19,7 @@ napi-shared = { path = "../napi-shared" } serde = "1" serde_derive = "1" serde_json = "1" +indexmap = "2" [target.'cfg(not(target_family = "wasm"))'.dependencies] napi = { path = "../../crates/napi", default-features = false, features = [ @@ -27,6 +28,7 @@ napi = { path = "../../crates/napi", default-features = false, features = [ "experimental", "latin1", "chrono_date", + "object_indexmap", "tokio", "async", "tokio_rt", @@ -43,6 +45,7 @@ napi = { path = "../../crates/napi", default-features = false, features = [ "experimental", "latin1", "chrono_date", + "object_indexmap", "tokio", "async", "tokio_rt", diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md index 3b6b9133..9c63b059 100644 --- a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md +++ b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md @@ -392,6 +392,8 @@ Generated by [AVA](https://avajs.dev). ␊ export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): void␊ ␊ + export function getBtreeMapping(): BTreeMap␊ + ␊ export function getBuffer(): Buffer␊ ␊ 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 getIndexMapping(): IndexMap␊ + ␊ export function getMapping(): Record␊ ␊ export function getModuleFileName(): string␊ @@ -425,6 +429,8 @@ Generated by [AVA](https://avajs.dev). ␊ export function getWords(): Array␊ ␊ + export function indexmapPassthrough(fixture: IndexMap): IndexMap␊ + ␊ /** default enum values are continuos i32s start from 0 */␊ export const enum Kind {␊ /** Barks */␊ @@ -544,6 +550,10 @@ Generated by [AVA](https://avajs.dev). name: string␊ }␊ ␊ + export function sumBtreeMapping(nums: BTreeMap): number␊ + ␊ + export function sumIndexMapping(nums: IndexMap): number␊ + ␊ export function sumMapping(nums: Record): number␊ ␊ export function sumNums(nums: Array): number␊ diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap index 8c15e912..f74ab62b 100644 Binary files a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap and b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap differ diff --git a/examples/napi/__tests__/values.spec.ts b/examples/napi/__tests__/values.spec.ts index 4de2771b..7807e55c 100644 --- a/examples/napi/__tests__/values.spec.ts +++ b/examples/napi/__tests__/values.spec.ts @@ -34,6 +34,11 @@ const { sumNums, getMapping, sumMapping, + getBtreeMapping, + sumBtreeMapping, + getIndexMapping, + sumIndexMapping, + indexmapPassthrough, getCwd, Animal, Kind, @@ -202,6 +207,11 @@ test('array', (t) => { test('map', (t) => { t.deepEqual(getMapping(), { a: 101, b: 102 }) 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) => { diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 72b926a1..8dd4e2f9 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -382,6 +382,8 @@ export function fibonacci(n: number): number export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): void +export function getBtreeMapping(): BTreeMap + export function getBuffer(): Buffer export function getCwd(callback: (arg0: string) => void): void @@ -392,6 +394,8 @@ export function getExternal(external: ExternalObject): number export function getGlobal(): typeof global +export function getIndexMapping(): IndexMap + export function getMapping(): Record export function getModuleFileName(): string @@ -415,6 +419,8 @@ export function getUndefined(): void export function getWords(): Array +export function indexmapPassthrough(fixture: IndexMap): IndexMap + /** default enum values are continuos i32s start from 0 */ export const enum Kind { /** Barks */ @@ -534,6 +540,10 @@ export interface StrictObject { name: string } +export function sumBtreeMapping(nums: BTreeMap): number + +export function sumIndexMapping(nums: IndexMap): number + export function sumMapping(nums: Record): number export function sumNums(nums: Array): number diff --git a/examples/napi/src/map.rs b/examples/napi/src/map.rs index c4d0bd43..d2f659be 100644 --- a/examples/napi/src/map.rs +++ b/examples/napi/src/map.rs @@ -1,4 +1,6 @@ -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; + +use indexmap::IndexMap; #[napi] fn get_mapping() -> HashMap { @@ -12,3 +14,34 @@ fn get_mapping() -> HashMap { fn sum_mapping(nums: HashMap) -> u32 { nums.into_values().sum() } + +#[napi] +fn get_btree_mapping() -> BTreeMap { + 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) -> u32 { + nums.into_values().sum() +} + +#[napi] +fn get_index_mapping() -> IndexMap { + 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) -> u32 { + nums.into_values().sum() +} + +#[napi] +fn indexmap_passthrough(fixture: IndexMap) -> IndexMap { + fixture +}