1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
use crate::{bindgen_prelude::*, check_status, sys, type_of, JsObject, ValueType};
use std::{ffi::CString, ptr};

pub type Object = JsObject;

impl Object {
  #[cfg(feature = "serde-json")]
  pub(crate) fn new(env: sys::napi_env) -> Result<Self> {
    let mut ptr = ptr::null_mut();
    unsafe {
      check_status!(
        sys::napi_create_object(env, &mut ptr),
        "Failed to create napi Object"
      )?;
    }

    Ok(Self(crate::Value {
      env,
      value: ptr,
      value_type: ValueType::Object,
    }))
  }

  pub fn get<K: AsRef<str>, V: FromNapiValue>(&self, field: K) -> Result<Option<V>> {
    let c_field = CString::new(field.as_ref())?;

    unsafe {
      let mut ret = ptr::null_mut();

      check_status!(
        sys::napi_get_named_property(self.0.env, self.0.value, c_field.as_ptr(), &mut ret),
        "Failed to get property with field `{}`",
        field.as_ref(),
      )?;

      let ty = type_of!(self.0.env, ret)?;

      Ok(if ty == ValueType::Undefined {
        None
      } else {
        Some(V::from_napi_value(self.0.env, ret)?)
      })
    }
  }

  pub fn set<K: AsRef<str>, V: ToNapiValue>(&mut self, field: K, val: V) -> Result<()> {
    let c_field = CString::new(field.as_ref())?;

    unsafe {
      let napi_val = V::to_napi_value(self.0.env, val)?;

      check_status!(
        sys::napi_set_named_property(self.0.env, self.0.value, c_field.as_ptr(), napi_val),
        "Failed to set property with field `{}`",
        c_field.to_string_lossy(),
      )?;

      Ok(())
    }
  }

  pub fn keys(obj: &Object) -> Result<Vec<String>> {
    let mut names = ptr::null_mut();
    unsafe {
      check_status!(
        sys::napi_get_property_names(obj.0.env, obj.0.value, &mut names),
        "Failed to get property names of given object"
      )?;
    }

    let names = unsafe { Array::from_napi_value(obj.0.env, names)? };
    let mut ret = vec![];

    for i in 0..names.len() {
      ret.push(names.get::<String>(i)?.unwrap());
    }

    Ok(ret)
  }
}

impl TypeName for Object {
  fn type_name() -> &'static str {
    "Object"
  }

  fn value_type() -> ValueType {
    ValueType::Object
  }
}

impl ValidateNapiValue for JsObject {}