1use std::{marker::PhantomData, ptr};
2
3use crate::{bindgen_prelude::*, check_status, Value};
4
5#[derive(Clone, Copy)]
6pub struct Array<'env> {
7 pub(crate) env: sys::napi_env,
8 pub(crate) inner: sys::napi_value,
9 pub(crate) len: u32,
10 _marker: std::marker::PhantomData<&'env ()>,
11}
12
13impl<'env> Array<'env> {
14 pub(crate) fn new(env: sys::napi_env, len: u32) -> Result<Self> {
15 let mut ptr = ptr::null_mut();
16 unsafe {
17 check_status!(
18 sys::napi_create_array_with_length(env, len as usize, &mut ptr),
19 "Failed to create napi Array"
20 )?;
21 }
22
23 Ok(Array {
24 env,
25 inner: ptr,
26 len,
27 _marker: std::marker::PhantomData,
28 })
29 }
30
31 pub fn get<T: FromNapiValue>(&self, index: u32) -> Result<Option<T>> {
32 if index >= self.len() {
33 return Ok(None);
34 }
35
36 let mut ret = ptr::null_mut();
37 unsafe {
38 check_status!(
39 sys::napi_get_element(self.env, self.inner, index, &mut ret),
40 "Failed to get element with index `{}`",
41 index,
42 )?;
43
44 Ok(Some(T::from_napi_value(self.env, ret)?))
45 }
46 }
47
48 pub fn get_ref<T: 'static + FromNapiRef>(&self, index: u32) -> Result<Option<&'env T>> {
49 if index >= self.len() {
50 return Ok(None);
51 }
52
53 let mut ret = ptr::null_mut();
54 unsafe {
55 check_status!(
56 sys::napi_get_element(self.env, self.inner, index, &mut ret),
57 "Failed to get element with index `{}`",
58 index,
59 )?;
60
61 Ok(Some(T::from_napi_ref(self.env, ret)?))
62 }
63 }
64
65 pub fn set<T: ToNapiValue>(&mut self, index: u32, val: T) -> Result<()> {
66 unsafe {
67 let napi_val = T::to_napi_value(self.env, val)?;
68
69 check_status!(
70 sys::napi_set_element(self.env, self.inner, index, napi_val),
71 "Failed to set element with index `{}`",
72 index,
73 )?;
74
75 if index >= self.len() {
76 self.len = index + 1;
77 }
78
79 Ok(())
80 }
81 }
82
83 pub fn insert<T: ToNapiValue>(&mut self, val: T) -> Result<()> {
84 self.set(self.len(), val)?;
85 Ok(())
86 }
87
88 #[allow(clippy::len_without_is_empty)]
89 pub fn len(&self) -> u32 {
90 self.len
91 }
92
93 pub fn coerce_to_object(self) -> Result<Object<'env>> {
94 let mut new_raw_value = ptr::null_mut();
95 check_status!(unsafe { sys::napi_coerce_to_object(self.env, self.inner, &mut new_raw_value) })?;
96 Ok(Object(
97 Value {
98 env: self.env,
99 value: new_raw_value,
100 value_type: ValueType::Object,
101 },
102 PhantomData,
103 ))
104 }
105}
106
107impl TypeName for Array<'_> {
108 fn type_name() -> &'static str {
109 "Array"
110 }
111
112 fn value_type() -> ValueType {
113 ValueType::Object
114 }
115}
116
117impl<'env> JsValue<'env> for Array<'env> {
118 fn value(&self) -> Value {
119 Value {
120 env: self.env,
121 value: self.inner,
122 value_type: ValueType::Object,
123 }
124 }
125}
126
127impl<'env> JsObjectValue<'env> for Array<'env> {}
128
129impl FromNapiValue for Array<'_> {
130 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
131 let mut len = 0;
132
133 check_status!(
134 unsafe { sys::napi_get_array_length(env, napi_val, &mut len) },
135 "Failed to get Array length",
136 )?;
137
138 Ok(Array {
139 inner: napi_val,
140 env,
141 len,
142 _marker: std::marker::PhantomData,
143 })
144 }
145}
146
147impl<'env> ValidateNapiValue for Array<'env> {
148 unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result<sys::napi_value> {
149 let mut is_array = false;
150 check_status!(
151 unsafe { sys::napi_is_array(env, napi_val, &mut is_array) },
152 "Failed to check given napi value is array"
153 )?;
154 if !is_array {
155 return Err(Error::new(
156 Status::InvalidArg,
157 "Expected an array".to_owned(),
158 ));
159 }
160 Ok(ptr::null_mut())
161 }
162}
163
164impl Array<'_> {
165 pub fn from_vec<T>(env: &Env, value: Vec<T>) -> Result<Self>
167 where
168 T: ToNapiValue,
169 {
170 let mut arr = Array::new(env.0, value.len() as u32)?;
171 value.into_iter().enumerate().try_for_each(|(index, val)| {
172 arr.set(index as u32, val)?;
173 Ok::<(), Error>(())
174 })?;
175 Ok(arr)
176 }
177
178 pub fn from_ref_vec_string(env: &Env, value: &[String]) -> Result<Self> {
180 let mut arr = Array::new(env.0, value.len() as u32)?;
181 value.iter().enumerate().try_for_each(|(index, val)| {
182 arr.set(index as u32, val.as_str())?;
183 Ok::<(), Error>(())
184 })?;
185 Ok(arr)
186 }
187
188 pub fn from_ref_vec<T>(env: &Env, value: &[T]) -> Result<Self>
190 where
191 T: ToNapiValue + Copy,
192 {
193 let mut arr = Array::new(env.0, value.len() as u32)?;
194 value.iter().enumerate().try_for_each(|(index, val)| {
195 arr.set(index as u32, *val)?;
196 Ok::<(), Error>(())
197 })?;
198 Ok(arr)
199 }
200}
201
202impl<T> TypeName for Vec<T> {
203 fn type_name() -> &'static str {
204 "Array<T>"
205 }
206
207 fn value_type() -> ValueType {
208 ValueType::Object
209 }
210}
211
212impl<T, const N: usize> ToNapiValue for [T; N]
213where
214 T: ToNapiValue + Copy,
215{
216 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
217 let mut arr = Array::new(env, val.len() as u32)?;
218
219 for (i, v) in val.into_iter().enumerate() {
220 arr.set(i as u32, v)?;
221 }
222
223 unsafe { Array::to_napi_value(env, arr) }
224 }
225}
226
227impl<T, const N: usize> ToNapiValue for &[T; N]
228where
229 for<'a> &'a T: ToNapiValue,
230{
231 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
232 let mut arr = Array::new(env, val.len() as u32)?;
233
234 for (i, v) in val.iter().enumerate() {
235 arr.set(i as u32, v)?;
236 }
237
238 unsafe { Array::to_napi_value(env, arr) }
239 }
240}
241
242impl<T> ToNapiValue for Vec<T>
243where
244 T: ToNapiValue,
245{
246 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
247 let mut arr = Array::new(env, val.len() as u32)?;
248
249 for (i, v) in val.into_iter().enumerate() {
250 arr.set(i as u32, v)?;
251 }
252
253 unsafe { Array::to_napi_value(env, arr) }
254 }
255}
256
257impl<T> ToNapiValue for &Vec<T>
258where
259 for<'a> &'a T: ToNapiValue,
260{
261 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
262 let mut arr = Array::new(env, val.len() as u32)?;
263
264 for (i, v) in val.iter().enumerate() {
265 arr.set(i as u32, v)?;
266 }
267
268 unsafe { Array::to_napi_value(env, arr) }
269 }
270}
271
272impl<T> ToNapiValue for &mut Vec<T>
273where
274 for<'a> &'a T: ToNapiValue,
275{
276 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
277 ToNapiValue::to_napi_value(env, &*val)
278 }
279}
280
281impl<T> FromNapiValue for Vec<T>
282where
283 T: FromNapiValue,
284{
285 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
286 let arr = unsafe { Array::from_napi_value(env, napi_val)? };
287 let mut vec = Vec::with_capacity(arr.len() as usize);
288
289 for i in 0..arr.len() {
290 if let Some(val) = arr.get::<T>(i)? {
291 vec.push(val);
292 } else {
293 return Err(Error::new(
294 Status::InvalidArg,
295 "Found inconsistent data type in Array<T> when converting to Rust Vec<T>".to_owned(),
296 ));
297 }
298 }
299
300 Ok(vec)
301 }
302}
303
304impl<T> ValidateNapiValue for Vec<T>
305where
306 T: FromNapiValue,
307{
308 unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result<sys::napi_value> {
309 let mut is_array = false;
310 check_status!(
311 unsafe { sys::napi_is_array(env, napi_val, &mut is_array) },
312 "Failed to check given napi value is array"
313 )?;
314 if !is_array {
315 return Err(Error::new(
316 Status::InvalidArg,
317 "Expected an array".to_owned(),
318 ));
319 }
320 Ok(ptr::null_mut())
321 }
322}
323
324macro_rules! arr_get {
325 ($arr:expr, $n:expr, $err:expr) => {
326 if let Some(e) = $arr.get($n)? {
327 e
328 } else {
329 return $err($n);
330 }
331 };
332}
333
334macro_rules! tuple_from_napi_value {
335 ($total:expr, $($n:expr),+,) => {
336 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
337 let arr = unsafe { Array::from_napi_value(env, napi_val)? };
338 let err = |v| Err(Error::new(
339 Status::InvalidArg,
340 format!(
341 "Found inconsistent data type in Array[{}] when converting to Rust T",
342 v
343 )
344 .to_owned(),
345 ));
346 if arr.len() < $total {
347 return Err(Error::new(
348 Status::InvalidArg,
349 format!("Array length < {}",$total).to_owned(),
350 ));
351 }
352 Ok(($(arr_get!(arr,$n,err),)+))
353 }
354 }
355}
356
357macro_rules! impl_tuple_validate_napi_value {
358 ($($ident:ident),+) => {
359 impl<$($ident: FromNapiValue),*> ValidateNapiValue for ($($ident,)*) {}
360 impl<$($ident: FromNapiValue),*> TypeName for ($($ident,)*) {
361 fn type_name() -> &'static str {
362 concat!("Tuple", "(", $(stringify!($ident), ","),*, ")")
363 }
364 fn value_type() -> ValueType {
365 ValueType::Object
366 }
367 }
368 };
369}
370
371macro_rules! impl_from_tuple {
372 (
373 $($typs:ident),*;
374 $($tidents:expr),+;
375 $length:expr
376 ) => {
377 impl<$($typs),*> FromNapiValue for ($($typs,)*)
378 where $($typs: FromNapiValue,)* {
379 tuple_from_napi_value!($length, $($tidents,)*);
380 }
381 };
382}
383
384macro_rules! impl_to_tuple {
385 (
386 $($typs:ident),*;
387 $($tidents:expr),+;
388 $length:expr
389 ) => {
390 impl<$($typs),*> ToNapiValue for ($($typs,)*)
391 where $($typs: ToNapiValue,)* {
392 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
393 let mut arr = Array::new(env, $length as u32)?;
394
395 #[allow(non_snake_case)]
396 let ($($typs,)*) = val;
397 let mut i = 0;
398
399 $(i+=1; unsafe {arr.set(i-1, <$typs as ToNapiValue>::to_napi_value(env, $typs)? )?}; )*
400
401 unsafe { Array::to_napi_value(env, arr) }
402 }
403 }
404 };
405}
406
407macro_rules! impl_tuples {
408 (
409 ;;$length:expr,
410 $shift:expr
411 ) => {};
412 (
413 $typ:ident$(, $($typs:ident),*)?;
414 $tident:expr$(, $($tidents:expr),*)?;
415 $length:expr,
416 $shift:expr
417 ) => {
418 impl_tuples!(
419 $($($typs),*)?;
420 $($($tidents),*)?;
421 $length - 1,
422 $shift + 1
423 );
424 impl_from_tuple!(
425 $typ$(, $($typs),*)?;
426 $tident - $shift$(, $($tidents - $shift),*)?;
427 $length
428 );
429 impl_to_tuple!(
430 $typ$(, $($typs),*)?;
431 $tident - $shift$(, $($tidents - $shift),*)?;
432 $length
433 );
434 impl_tuple_validate_napi_value!($typ$(, $($typs),*)?);
435 };
436}
437
438impl_tuples!(
439 T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15;
440 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15;
441 16, 0
442);