napi/bindgen_runtime/js_values/
array.rs

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 is_arr = false;
132    check_status!(
133      unsafe { sys::napi_is_array(env, napi_val, &mut is_arr) },
134      "Failed to check given napi value is array"
135    )?;
136
137    if is_arr {
138      let mut len = 0;
139
140      check_status!(
141        unsafe { sys::napi_get_array_length(env, napi_val, &mut len) },
142        "Failed to get Array length",
143      )?;
144
145      Ok(Array {
146        inner: napi_val,
147        env,
148        len,
149        _marker: std::marker::PhantomData,
150      })
151    } else {
152      Err(Error::new(
153        Status::InvalidArg,
154        "Given napi value is not an array".to_owned(),
155      ))
156    }
157  }
158}
159
160impl Array<'_> {
161  /// Create `Array` from `Vec<T>`
162  pub fn from_vec<T>(env: &Env, value: Vec<T>) -> Result<Self>
163  where
164    T: ToNapiValue,
165  {
166    let mut arr = Array::new(env.0, value.len() as u32)?;
167    value.into_iter().enumerate().try_for_each(|(index, val)| {
168      arr.set(index as u32, val)?;
169      Ok::<(), Error>(())
170    })?;
171    Ok(arr)
172  }
173
174  /// Create `Array` from `&Vec<String>`
175  pub fn from_ref_vec_string(env: &Env, value: &[String]) -> Result<Self> {
176    let mut arr = Array::new(env.0, value.len() as u32)?;
177    value.iter().enumerate().try_for_each(|(index, val)| {
178      arr.set(index as u32, val.as_str())?;
179      Ok::<(), Error>(())
180    })?;
181    Ok(arr)
182  }
183
184  /// Create `Array` from `&Vec<T: Copy + ToNapiValue>`
185  pub fn from_ref_vec<T>(env: &Env, value: &[T]) -> Result<Self>
186  where
187    T: ToNapiValue + Copy,
188  {
189    let mut arr = Array::new(env.0, value.len() as u32)?;
190    value.iter().enumerate().try_for_each(|(index, val)| {
191      arr.set(index as u32, *val)?;
192      Ok::<(), Error>(())
193    })?;
194    Ok(arr)
195  }
196}
197
198impl ValidateNapiValue for Array<'_> {}
199
200impl<T> TypeName for Vec<T> {
201  fn type_name() -> &'static str {
202    "Array<T>"
203  }
204
205  fn value_type() -> ValueType {
206    ValueType::Object
207  }
208}
209
210impl<T, const N: usize> ToNapiValue for [T; N]
211where
212  T: ToNapiValue + Copy,
213{
214  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
215    let mut arr = Array::new(env, val.len() as u32)?;
216
217    for (i, v) in val.into_iter().enumerate() {
218      arr.set(i as u32, v)?;
219    }
220
221    unsafe { Array::to_napi_value(env, arr) }
222  }
223}
224
225impl<T, const N: usize> ToNapiValue for &[T; N]
226where
227  for<'a> &'a T: ToNapiValue,
228{
229  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
230    let mut arr = Array::new(env, val.len() as u32)?;
231
232    for (i, v) in val.iter().enumerate() {
233      arr.set(i as u32, v)?;
234    }
235
236    unsafe { Array::to_napi_value(env, arr) }
237  }
238}
239
240impl<T> ToNapiValue for Vec<T>
241where
242  T: ToNapiValue,
243{
244  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
245    let mut arr = Array::new(env, val.len() as u32)?;
246
247    for (i, v) in val.into_iter().enumerate() {
248      arr.set(i as u32, v)?;
249    }
250
251    unsafe { Array::to_napi_value(env, arr) }
252  }
253}
254
255impl<T> ToNapiValue for &Vec<T>
256where
257  for<'a> &'a T: ToNapiValue,
258{
259  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
260    let mut arr = Array::new(env, val.len() as u32)?;
261
262    for (i, v) in val.iter().enumerate() {
263      arr.set(i as u32, v)?;
264    }
265
266    unsafe { Array::to_napi_value(env, arr) }
267  }
268}
269
270impl<T> ToNapiValue for &mut Vec<T>
271where
272  for<'a> &'a T: ToNapiValue,
273{
274  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
275    ToNapiValue::to_napi_value(env, &*val)
276  }
277}
278
279impl<T> FromNapiValue for Vec<T>
280where
281  T: FromNapiValue,
282{
283  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
284    let arr = unsafe { Array::from_napi_value(env, napi_val)? };
285    let mut vec = Vec::with_capacity(arr.len() as usize);
286
287    for i in 0..arr.len() {
288      if let Some(val) = arr.get::<T>(i)? {
289        vec.push(val);
290      } else {
291        return Err(Error::new(
292          Status::InvalidArg,
293          "Found inconsistent data type in Array<T> when converting to Rust Vec<T>".to_owned(),
294        ));
295      }
296    }
297
298    Ok(vec)
299  }
300}
301
302impl<T> ValidateNapiValue for Vec<T>
303where
304  T: FromNapiValue,
305{
306  unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result<sys::napi_value> {
307    let mut is_array = false;
308    check_status!(
309      unsafe { sys::napi_is_array(env, napi_val, &mut is_array) },
310      "Failed to check given napi value is array"
311    )?;
312    if !is_array {
313      return Err(Error::new(
314        Status::InvalidArg,
315        "Expected an array".to_owned(),
316      ));
317    }
318    Ok(ptr::null_mut())
319  }
320}
321
322macro_rules! arr_get {
323  ($arr:expr, $n:expr, $err:expr) => {
324    if let Some(e) = $arr.get($n)? {
325      e
326    } else {
327      return $err($n);
328    }
329  };
330}
331
332macro_rules! tuple_from_napi_value {
333  ($total:expr, $($n:expr),+,) => {
334    unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
335      let arr = unsafe { Array::from_napi_value(env, napi_val)? };
336      let err = |v| Err(Error::new(
337        Status::InvalidArg,
338        format!(
339          "Found inconsistent data type in Array[{}] when converting to Rust T",
340          v
341        )
342        .to_owned(),
343      ));
344      if arr.len() < $total {
345        return Err(Error::new(
346            Status::InvalidArg,
347            format!("Array length < {}",$total).to_owned(),
348        ));
349      }
350      Ok(($(arr_get!(arr,$n,err)),+))
351    }
352  }
353}
354
355macro_rules! impl_tuple_validate_napi_value {
356  ($($ident:ident),+) => {
357    impl<$($ident: FromNapiValue),*> ValidateNapiValue for ($($ident,)*) {}
358    impl<$($ident: FromNapiValue),*> TypeName for ($($ident,)*) {
359      fn type_name() -> &'static str {
360        concat!("Tuple", "(", $(stringify!($ident), ","),*, ")")
361      }
362      fn value_type() -> ValueType {
363        ValueType::Object
364      }
365    }
366  };
367}
368
369macro_rules! impl_from_tuple {
370  (
371    $($typs:ident),*;
372    $($tidents:expr),+;
373    $length:expr
374  ) => {
375    impl<$($typs),*> FromNapiValue for ($($typs,)*)
376      where $($typs: FromNapiValue,)* {
377      tuple_from_napi_value!($length, $($tidents,)*);
378    }
379  };
380}
381
382macro_rules! impl_to_tuple {
383  (
384    $($typs:ident),*;
385    $($tidents:expr),+;
386    $length:expr
387  ) => {
388    impl<$($typs),*> ToNapiValue for ($($typs,)*)
389      where $($typs: ToNapiValue,)* {
390      unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
391        let mut arr = Array::new(env, $length as u32)?;
392
393        #[allow(non_snake_case)]
394        let ($($typs,)*) = val;
395        let mut i = 0;
396
397        $(i+=1; unsafe {arr.set(i-1, <$typs as ToNapiValue>::to_napi_value(env, $typs)? )?}; )*
398
399        unsafe { Array::to_napi_value(env, arr) }
400      }
401    }
402  };
403}
404
405macro_rules! impl_tuples {
406  (
407    ;;$length:expr,
408    $shift:expr
409  ) => {};
410  (
411    $typ:ident$(, $($typs:ident),*)?;
412    $tident:expr$(, $($tidents:expr),*)?;
413    $length:expr,
414    $shift:expr
415  ) => {
416    impl_tuples!(
417      $($($typs),*)?;
418      $($($tidents),*)?;
419      $length - 1,
420      $shift + 1
421    );
422    impl_from_tuple!(
423      $typ$(, $($typs),*)?;
424      $tident - $shift$(, $($tidents - $shift),*)?;
425      $length
426    );
427    impl_to_tuple!(
428      $typ$(, $($typs),*)?;
429      $tident - $shift$(, $($tidents - $shift),*)?;
430      $length
431    );
432    impl_tuple_validate_napi_value!($typ$(, $($typs),*)?);
433  };
434}
435
436impl_tuples!(
437  T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15;
438  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15;
439  16, 0
440);