feat: impl FromNapiValue
for serde_json::Number
, fix it for Null
, throw for impossible types (#1052)
fix #1013 Co-authored-by: zeroslope <jsx55242@foxmail.com>
This commit is contained in:
parent
c001038852
commit
ed12bd76bd
6 changed files with 117 additions and 22 deletions
|
@ -1,4 +1,4 @@
|
|||
use serde_json::{Map, Value};
|
||||
use serde_json::{Map, Number, Value};
|
||||
|
||||
use crate::{
|
||||
bindgen_runtime::Null, check_status, sys, type_of, Error, JsObject, Result, Status, ValueType,
|
||||
|
@ -11,20 +11,7 @@ impl ToNapiValue for Value {
|
|||
match val {
|
||||
Value::Null => unsafe { Null::to_napi_value(env, Null) },
|
||||
Value::Bool(b) => unsafe { bool::to_napi_value(env, b) },
|
||||
Value::Number(n) => {
|
||||
if n.is_i64() {
|
||||
unsafe { i64::to_napi_value(env, n.as_i64().unwrap()) }
|
||||
} else if n.is_f64() {
|
||||
unsafe { f64::to_napi_value(env, n.as_f64().unwrap()) }
|
||||
} else {
|
||||
let n = n.as_u64().unwrap();
|
||||
if n > u32::MAX as u64 {
|
||||
todo!("impl BigInt")
|
||||
} else {
|
||||
unsafe { u32::to_napi_value(env, n as u32) }
|
||||
}
|
||||
}
|
||||
}
|
||||
Value::Number(n) => unsafe { Number::to_napi_value(env, n) },
|
||||
Value::String(s) => unsafe { String::to_napi_value(env, s) },
|
||||
Value::Array(arr) => unsafe { Vec::<Value>::to_napi_value(env, arr) },
|
||||
Value::Object(obj) => unsafe { Map::to_napi_value(env, obj) },
|
||||
|
@ -37,12 +24,7 @@ impl FromNapiValue for Value {
|
|||
let ty = type_of!(env, napi_val)?;
|
||||
let val = match ty {
|
||||
ValueType::Boolean => Value::Bool(unsafe { bool::from_napi_value(env, napi_val)? }),
|
||||
ValueType::Number => {
|
||||
return Err(Error::new(
|
||||
Status::InvalidArg,
|
||||
"Js Number is not be able to convert to rust.".to_owned(),
|
||||
));
|
||||
}
|
||||
ValueType::Number => Value::Number(unsafe { Number::from_napi_value(env, napi_val)? }),
|
||||
ValueType::String => Value::String(unsafe { String::from_napi_value(env, napi_val)? }),
|
||||
ValueType::Object => {
|
||||
let mut is_arr = false;
|
||||
|
@ -59,7 +41,37 @@ impl FromNapiValue for Value {
|
|||
}
|
||||
#[cfg(feature = "napi6")]
|
||||
ValueType::BigInt => todo!(),
|
||||
_ => Value::Null,
|
||||
ValueType::Null => Value::Null,
|
||||
ValueType::Function => {
|
||||
return Err(Error::new(
|
||||
Status::InvalidArg,
|
||||
"JS functions cannot be represented as a serde_json::Value".to_owned(),
|
||||
))
|
||||
}
|
||||
ValueType::Undefined => {
|
||||
return Err(Error::new(
|
||||
Status::InvalidArg,
|
||||
"undefined cannot be represented as a serde_json::Value".to_owned(),
|
||||
))
|
||||
}
|
||||
ValueType::Symbol => {
|
||||
return Err(Error::new(
|
||||
Status::InvalidArg,
|
||||
"JS symbols cannot be represented as a serde_json::Value".to_owned(),
|
||||
))
|
||||
}
|
||||
ValueType::External => {
|
||||
return Err(Error::new(
|
||||
Status::InvalidArg,
|
||||
"External JS objects cannot be represented as a serde_json::Value".to_owned(),
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
Status::InvalidArg,
|
||||
"Unknown JS variables cannot be represented as a serde_json::Value".to_owned(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(val)
|
||||
|
@ -96,3 +108,50 @@ impl FromNapiValue for Map<String, Value> {
|
|||
Ok(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToNapiValue for Number {
|
||||
unsafe fn to_napi_value(env: sys::napi_env, n: Self) -> Result<sys::napi_value> {
|
||||
if n.is_i64() {
|
||||
unsafe { i64::to_napi_value(env, n.as_i64().unwrap()) }
|
||||
} else if n.is_f64() {
|
||||
unsafe { f64::to_napi_value(env, n.as_f64().unwrap()) }
|
||||
} else {
|
||||
let n = n.as_u64().unwrap();
|
||||
if n > u32::MAX as u64 {
|
||||
todo!("impl BigInt")
|
||||
} else {
|
||||
unsafe { u32::to_napi_value(env, n as u32) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromNapiValue for Number {
|
||||
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
|
||||
let n = unsafe { f64::from_napi_value(env, napi_val)? };
|
||||
// Try to auto-convert to integers
|
||||
let n = if n.trunc() == n {
|
||||
if n >= 0.0f64 && n <= u32::MAX as f64 {
|
||||
// This can be represented as u32
|
||||
Some(Number::from(n as u32))
|
||||
} else if n < 0.0f64 && n >= i32::MIN as f64 {
|
||||
Some(Number::from(n as i32))
|
||||
} else {
|
||||
// must be a float
|
||||
Number::from_f64(n)
|
||||
}
|
||||
} else {
|
||||
// must be a float
|
||||
Number::from_f64(n)
|
||||
};
|
||||
|
||||
let n = n.ok_or_else(|| {
|
||||
Error::new(
|
||||
Status::InvalidArg,
|
||||
"Failed to convert js number to serde_json::Number".to_owned(),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,6 +123,7 @@ Generated by [AVA](https://avajs.dev).
|
|||
}␊
|
||||
export function readPackageJson(): PackageJson␊
|
||||
export function getPackageJsonName(packageJson: PackageJson): string␊
|
||||
export function testSerdeRoundtrip(data: any): any␊
|
||||
export function contains(source: string, target: string): boolean␊
|
||||
export function concatStr(s: string): string␊
|
||||
export function concatUtf16(s: string): string␊
|
||||
|
|
Binary file not shown.
|
@ -80,6 +80,7 @@ import {
|
|||
receiveMutClassOrNumber,
|
||||
getStrFromObject,
|
||||
returnJsFunction,
|
||||
testSerdeRoundtrip,
|
||||
} from '../'
|
||||
|
||||
test('export const', (t) => {
|
||||
|
@ -299,6 +300,34 @@ test('serde-json', (t) => {
|
|||
t.is(getPackageJsonName(packageJson), 'napi-rs')
|
||||
})
|
||||
|
||||
test('serde-roundtrip', (t) => {
|
||||
t.is(testSerdeRoundtrip(1), 1)
|
||||
t.is(testSerdeRoundtrip(1.2), 1.2)
|
||||
t.is(testSerdeRoundtrip(-1), -1)
|
||||
|
||||
t.deepEqual(testSerdeRoundtrip([1, 1.2, -1]), [1, 1.2, -1])
|
||||
t.deepEqual(testSerdeRoundtrip({ a: 1, b: 1.2, c: -1 }), {
|
||||
a: 1,
|
||||
b: 1.2,
|
||||
c: -1,
|
||||
})
|
||||
t.throws(() => testSerdeRoundtrip(NaN))
|
||||
|
||||
t.is(testSerdeRoundtrip(null), null)
|
||||
|
||||
let err = t.throws(() => testSerdeRoundtrip(undefined))
|
||||
t.is(err!.message, 'undefined cannot be represented as a serde_json::Value')
|
||||
|
||||
err = t.throws(() => testSerdeRoundtrip(() => {}))
|
||||
t.is(
|
||||
err!.message,
|
||||
'JS functions cannot be represented as a serde_json::Value',
|
||||
)
|
||||
|
||||
err = t.throws(() => testSerdeRoundtrip(Symbol.for('foo')))
|
||||
t.is(err!.message, 'JS symbols cannot be represented as a serde_json::Value')
|
||||
})
|
||||
|
||||
test('buffer', (t) => {
|
||||
let buf = getBuffer()
|
||||
t.is(buf.toString('utf-8'), 'Hello world')
|
||||
|
|
1
examples/napi/index.d.ts
vendored
1
examples/napi/index.d.ts
vendored
|
@ -113,6 +113,7 @@ export interface PackageJson {
|
|||
}
|
||||
export function readPackageJson(): PackageJson
|
||||
export function getPackageJsonName(packageJson: PackageJson): string
|
||||
export function testSerdeRoundtrip(data: any): any
|
||||
export function contains(source: string, target: string): boolean
|
||||
export function concatStr(s: string): string
|
||||
export function concatUtf16(s: string): string
|
||||
|
|
|
@ -25,3 +25,8 @@ fn read_package_json() -> Result<PackageJson> {
|
|||
fn get_package_json_name(package_json: PackageJson) -> String {
|
||||
package_json.name
|
||||
}
|
||||
|
||||
#[napi]
|
||||
fn test_serde_roundtrip(data: Value) -> Value {
|
||||
data
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue