napi/bindgen_runtime/js_values/
map.rs1use 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}