napi_h/bindgen_runtime/js_values/
serde.rs

1use serde_json::{Map, Number, Value};
2
3use crate::{
4  bindgen_runtime::Null, check_status, sys, type_of, Error, JsObject, Result, Status, ValueType,
5};
6
7#[cfg(feature = "napi6")]
8use super::BigInt;
9use super::{FromNapiValue, Object, ToNapiValue};
10
11impl ToNapiValue for Value {
12  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
13    match val {
14      Value::Null => unsafe { Null::to_napi_value(env, Null) },
15      Value::Bool(b) => unsafe { bool::to_napi_value(env, b) },
16      Value::Number(n) => unsafe { Number::to_napi_value(env, n) },
17      Value::String(s) => unsafe { String::to_napi_value(env, s) },
18      Value::Array(arr) => unsafe { Vec::<Value>::to_napi_value(env, arr) },
19      Value::Object(obj) => unsafe { Map::to_napi_value(env, obj) },
20    }
21  }
22}
23
24impl FromNapiValue for Value {
25  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
26    let ty = type_of!(env, napi_val)?;
27    let val = match ty {
28      ValueType::Boolean => Value::Bool(unsafe { bool::from_napi_value(env, napi_val)? }),
29      ValueType::Number => Value::Number(unsafe { Number::from_napi_value(env, napi_val)? }),
30      ValueType::String => Value::String(unsafe { String::from_napi_value(env, napi_val)? }),
31      ValueType::Object => {
32        let mut is_arr = false;
33        check_status!(
34          unsafe { sys::napi_is_array(env, napi_val, &mut is_arr) },
35          "Failed to detect whether given js is an array"
36        )?;
37
38        if is_arr {
39          Value::Array(unsafe { Vec::<Value>::from_napi_value(env, napi_val)? })
40        } else {
41          Value::Object(unsafe { Map::<String, Value>::from_napi_value(env, napi_val)? })
42        }
43      }
44      #[cfg(feature = "napi6")]
45      ValueType::BigInt => todo!(),
46      ValueType::Null => Value::Null,
47      ValueType::Function => {
48        return Err(Error::new(
49          Status::InvalidArg,
50          "JS functions cannot be represented as a serde_json::Value".to_owned(),
51        ))
52      }
53      ValueType::Undefined => {
54        return Err(Error::new(
55          Status::InvalidArg,
56          "undefined cannot be represented as a serde_json::Value".to_owned(),
57        ))
58      }
59      ValueType::Symbol => {
60        return Err(Error::new(
61          Status::InvalidArg,
62          "JS symbols cannot be represented as a serde_json::Value".to_owned(),
63        ))
64      }
65      ValueType::External => {
66        return Err(Error::new(
67          Status::InvalidArg,
68          "External JS objects cannot be represented as a serde_json::Value".to_owned(),
69        ))
70      }
71      _ => {
72        return Err(Error::new(
73          Status::InvalidArg,
74          "Unknown JS variables cannot be represented as a serde_json::Value".to_owned(),
75        ))
76      }
77    };
78
79    Ok(val)
80  }
81}
82
83impl ToNapiValue for Map<String, Value> {
84  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
85    let mut obj = Object::new(env)?;
86
87    for (k, v) in val.into_iter() {
88      obj.set(k, v)?;
89    }
90
91    unsafe { Object::to_napi_value(env, obj) }
92  }
93}
94
95impl FromNapiValue for Map<String, Value> {
96  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
97    let obj = JsObject(crate::Value {
98      env,
99      value: napi_val,
100      value_type: ValueType::Object,
101    });
102
103    let mut map = Map::new();
104    for key in Object::keys(&obj)?.into_iter() {
105      if let Some(val) = obj.get(&key)? {
106        map.insert(key, val);
107      }
108    }
109
110    Ok(map)
111  }
112}
113
114impl ToNapiValue for Number {
115  unsafe fn to_napi_value(env: sys::napi_env, n: Self) -> Result<sys::napi_value> {
116    #[cfg(feature = "napi6")]
117    const MAX_SAFE_INT: i64 = 9007199254740991i64; // 2 ^ 53 - 1
118    if n.is_i64() {
119      let n = n.as_i64().unwrap();
120      #[cfg(feature = "napi6")]
121      {
122        if !(-MAX_SAFE_INT..=MAX_SAFE_INT).contains(&n) {
123          return unsafe { BigInt::to_napi_value(env, BigInt::from(n)) };
124        }
125      }
126
127      unsafe { i64::to_napi_value(env, n) }
128    } else if n.is_f64() {
129      unsafe { f64::to_napi_value(env, n.as_f64().unwrap()) }
130    } else {
131      let n = n.as_u64().unwrap();
132      if n > u32::MAX as u64 {
133        #[cfg(feature = "napi6")]
134        {
135          return unsafe { BigInt::to_napi_value(env, BigInt::from(n)) };
136        }
137
138        #[cfg(not(feature = "napi6"))]
139        return unsafe { String::to_napi_value(env, n.to_string()) };
140      } else {
141        unsafe { u32::to_napi_value(env, n as u32) }
142      }
143    }
144  }
145}
146
147impl FromNapiValue for Number {
148  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
149    let n = unsafe { f64::from_napi_value(env, napi_val)? };
150    // Try to auto-convert to integers
151    let n = if n.trunc() == n {
152      if n >= 0.0f64 && n <= u32::MAX as f64 {
153        // This can be represented as u32
154        Some(Number::from(n as u32))
155      } else if n < 0.0f64 && n >= i32::MIN as f64 {
156        Some(Number::from(n as i32))
157      } else {
158        // must be a float
159        Number::from_f64(n)
160      }
161    } else {
162      // must be a float
163      Number::from_f64(n)
164    };
165
166    let n = n.ok_or_else(|| {
167      Error::new(
168        Status::InvalidArg,
169        "Failed to convert js number to serde_json::Number".to_owned(),
170      )
171    })?;
172
173    Ok(n)
174  }
175}