Skip to main content

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 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  /// Create `Array` from `Vec<T>`
166  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  /// Create `Array` from `&Vec<String>`
179  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  /// Create `Array` from `&Vec<T: Copy + ToNapiValue>`
189  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);