napi/bindgen_runtime/js_values/
map.rs

1use std::collections::{BTreeMap, HashMap};
2use std::hash::{BuildHasher, Hash};
3
4#[cfg(feature = "object_indexmap")]
5use indexmap::IndexMap;
6
7use crate::bindgen_prelude::*;
8
9impl<K, V, S> TypeName for HashMap<K, V, S> {
10  fn type_name() -> &'static str {
11    "HashMap"
12  }
13
14  fn value_type() -> ValueType {
15    ValueType::Object
16  }
17}
18
19impl<K: From<String> + Eq + Hash, V: FromNapiValue, S> ValidateNapiValue for HashMap<K, V, S> {}
20
21impl<K, V, S> ToNapiValue for HashMap<K, V, S>
22where
23  K: AsRef<str>,
24  V: ToNapiValue,
25{
26  #[cfg(not(feature = "noop"))]
27  unsafe fn to_napi_value(raw_env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
28    let env = Env::from(raw_env);
29    #[cfg_attr(feature = "napi10", allow(unused_mut))]
30    let mut obj = Object::new(&env)?;
31    #[cfg(all(
32      feature = "napi10",
33      feature = "node_version_detect",
34      feature = "dyn-symbols",
35      not(feature = "noop"),
36    ))]
37    let node_version = NODE_VERSION.get().unwrap();
38    for (k, v) in val.into_iter() {
39      #[cfg(all(
40        feature = "napi10",
41        feature = "node_version_detect",
42        feature = "dyn-symbols",
43        not(feature = "noop"),
44      ))]
45      {
46        if node_version.major >= 20 && node_version.minor >= 18 {
47          fast_set_property(raw_env, obj.0.value, k, v)?;
48        } else {
49          obj.set(k.as_ref(), v)?;
50        }
51      }
52      #[cfg(not(all(
53        feature = "napi10",
54        feature = "node_version_detect",
55        feature = "dyn-symbols"
56      )))]
57      obj.set(k.as_ref(), v)?;
58    }
59
60    unsafe { Object::to_napi_value(raw_env, obj) }
61  }
62
63  #[cfg(feature = "noop")]
64  unsafe fn to_napi_value(_env: sys::napi_env, _val: Self) -> Result<sys::napi_value> {
65    unimplemented!("HashMap is not supported in noop mode");
66  }
67}
68
69impl<K, V, S> FromNapiValue for HashMap<K, V, S>
70where
71  K: From<String> + Eq + Hash,
72  V: FromNapiValue,
73  S: Default + BuildHasher,
74{
75  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
76    let obj = unsafe { Object::from_napi_value(env, napi_val)? };
77    let keys = Object::keys(&obj)?;
78    let mut map: HashMap<K, V, S> = HashMap::with_capacity_and_hasher(keys.len(), S::default());
79    for key in keys.into_iter() {
80      if let Some(val) = obj.get(&key)? {
81        map.insert(K::from(key), val);
82      }
83    }
84
85    Ok(map)
86  }
87}
88
89impl<K, V> TypeName for BTreeMap<K, V> {
90  fn type_name() -> &'static str {
91    "BTreeMap"
92  }
93
94  fn value_type() -> ValueType {
95    ValueType::Object
96  }
97}
98
99impl<K: From<String> + Ord, V: FromNapiValue> ValidateNapiValue for BTreeMap<K, V> {}
100
101impl<K, V> ToNapiValue for BTreeMap<K, V>
102where
103  K: AsRef<str>,
104  V: ToNapiValue,
105{
106  #[cfg(not(feature = "noop"))]
107  unsafe fn to_napi_value(raw_env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
108    let env = Env::from(raw_env);
109    #[cfg_attr(feature = "napi10", allow(unused_mut))]
110    let mut obj = Object::new(&env)?;
111    #[cfg(all(
112      feature = "napi10",
113      feature = "node_version_detect",
114      feature = "dyn-symbols",
115      not(feature = "noop"),
116    ))]
117    let node_version = NODE_VERSION.get().unwrap();
118    for (k, v) in val.into_iter() {
119      #[cfg(all(
120        feature = "napi10",
121        feature = "node_version_detect",
122        feature = "dyn-symbols",
123        not(feature = "noop"),
124      ))]
125      {
126        if node_version.major >= 20 && node_version.minor >= 18 {
127          fast_set_property(raw_env, obj.0.value, k, v)?;
128        } else {
129          obj.set(k.as_ref(), v)?;
130        }
131      }
132      #[cfg(not(all(
133        feature = "napi10",
134        feature = "node_version_detect",
135        feature = "dyn-symbols"
136      )))]
137      obj.set(k.as_ref(), v)?;
138    }
139
140    unsafe { Object::to_napi_value(raw_env, obj) }
141  }
142
143  #[cfg(feature = "noop")]
144  unsafe fn to_napi_value(_env: sys::napi_env, _val: Self) -> Result<sys::napi_value> {
145    unimplemented!("BTreeMap is not supported in noop mode");
146  }
147}
148
149impl<K, V> FromNapiValue for BTreeMap<K, V>
150where
151  K: From<String> + Ord,
152  V: FromNapiValue,
153{
154  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
155    let obj = unsafe { Object::from_napi_value(env, napi_val)? };
156    let mut map = BTreeMap::default();
157    for key in Object::keys(&obj)?.into_iter() {
158      if let Some(val) = obj.get(&key)? {
159        map.insert(K::from(key), val);
160      }
161    }
162
163    Ok(map)
164  }
165}
166
167#[cfg(feature = "object_indexmap")]
168impl<K, V, S> TypeName for IndexMap<K, V, S> {
169  fn type_name() -> &'static str {
170    "IndexMap"
171  }
172
173  fn value_type() -> ValueType {
174    ValueType::Object
175  }
176}
177
178#[cfg(feature = "object_indexmap")]
179impl<K: From<String> + Hash + Eq, V: FromNapiValue> ValidateNapiValue for IndexMap<K, V> {}
180
181#[cfg(feature = "object_indexmap")]
182impl<K, V, S> ToNapiValue for IndexMap<K, V, S>
183where
184  K: AsRef<str>,
185  V: ToNapiValue,
186  S: Default + BuildHasher,
187{
188  #[cfg(not(feature = "noop"))]
189  unsafe fn to_napi_value(raw_env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
190    let env = Env::from(raw_env);
191    #[cfg_attr(feature = "napi10", allow(unused_mut))]
192    let mut obj = Object::new(&env)?;
193    #[cfg(all(
194      feature = "napi10",
195      feature = "node_version_detect",
196      feature = "dyn-symbols",
197      not(feature = "noop"),
198    ))]
199    let node_version = NODE_VERSION.get().unwrap();
200    for (k, v) in val.into_iter() {
201      #[cfg(all(
202        feature = "napi10",
203        feature = "node_version_detect",
204        feature = "dyn-symbols",
205        not(feature = "noop"),
206      ))]
207      {
208        if node_version.major >= 20 && node_version.minor >= 18 {
209          fast_set_property(raw_env, obj.0.value, k, v)?;
210        } else {
211          obj.set(k.as_ref(), v)?;
212        }
213      }
214      #[cfg(not(all(
215        feature = "experimental",
216        feature = "node_version_detect",
217        feature = "dyn-symbols"
218      )))]
219      obj.set(k.as_ref(), v)?;
220    }
221
222    unsafe { Object::to_napi_value(raw_env, obj) }
223  }
224
225  #[cfg(feature = "noop")]
226  unsafe fn to_napi_value(_env: sys::napi_env, _val: Self) -> Result<sys::napi_value> {
227    unimplemented!("BTreeMap is not supported in noop mode");
228  }
229}
230
231#[cfg(feature = "object_indexmap")]
232impl<K, V, S> FromNapiValue for IndexMap<K, V, S>
233where
234  K: From<String> + Hash + Eq,
235  V: FromNapiValue,
236  S: Default + BuildHasher,
237{
238  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
239    let obj = unsafe { Object::from_napi_value(env, napi_val)? };
240    let mut map = IndexMap::default();
241    for key in Object::keys(&obj)?.into_iter() {
242      if let Some(val) = obj.get(&key)? {
243        map.insert(K::from(key), val);
244      }
245    }
246
247    Ok(map)
248  }
249}
250
251#[cfg(all(
252  feature = "napi10",
253  feature = "node_version_detect",
254  feature = "dyn-symbols",
255  not(feature = "noop"),
256))]
257fn fast_set_property<K: AsRef<str>, V: ToNapiValue>(
258  raw_env: sys::napi_env,
259  obj: sys::napi_value,
260  k: K,
261  v: V,
262) -> Result<()> {
263  let mut property_key = std::ptr::null_mut();
264  check_status!(
265    unsafe {
266      sys::node_api_create_property_key_utf8(
267        raw_env,
268        k.as_ref().as_ptr().cast(),
269        k.as_ref().len() as isize,
270        &mut property_key,
271      )
272    },
273    "Create property key failed"
274  )?;
275  check_status!(
276    unsafe { sys::napi_set_property(raw_env, obj, property_key, V::to_napi_value(raw_env, v)?,) },
277    "Failed to set property"
278  )?;
279  Ok(())
280}