napi_h/bindgen_runtime/js_values/
serde.rs1use 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; 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 let n = if n.trunc() == n {
152 if n >= 0.0f64 && n <= u32::MAX as f64 {
153 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 Number::from_f64(n)
160 }
161 } else {
162 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}