napi-rs/examples/napi-compat-mode/src/serde.rs
forehalo 2467b7139b
Introduce #[napi] procedural macro to automation development boilerplate (#696)
* napi procedural macro for basic rust/JavaScript types
* introduce the `compat-mode` for `napi` and `napi-derive` crates for backward compatible
* remove #[inline] and let compiler to decide the inline behavior
* cli now can produce the `.d.ts` file for native binding
* many tests and example for the new procedural macro

Co-authored-by: LongYinan <lynweklm@gmail.com>
2021-09-23 01:29:09 +08:00

217 lines
5.3 KiB
Rust

use napi::{CallContext, JsObject, JsString, JsUndefined, JsUnknown, Result};
use serde_json::{from_str, to_string};
#[derive(Serialize, Debug, Deserialize)]
struct AnObject {
a: u32,
b: Vec<f64>,
c: String,
}
#[derive(Serialize, Debug, Deserialize, Eq, PartialEq)]
struct Inner;
#[derive(Serialize, Debug, Deserialize, Eq, PartialEq)]
struct Inner2(i32, bool, String);
#[derive(Serialize, Debug, Deserialize, Eq, PartialEq)]
enum TypeEnum {
Empty,
Tuple(u32, String),
Struct { a: u8, b: Vec<u8> },
Value(Vec<char>),
}
#[derive(Serialize, Debug, Deserialize, PartialEq)]
struct AnObjectTwo {
a: u32,
b: Vec<i64>,
c: String,
d: Option<bool>,
e: Option<bool>,
f: Inner,
g: Inner2,
h: char,
i: TypeEnum,
j: TypeEnum,
k: TypeEnum,
l: String,
m: Vec<u8>,
o: TypeEnum,
p: Vec<f64>,
q: u128,
r: i128,
}
#[derive(Serialize, Debug, Deserialize)]
struct BytesObject<'a> {
#[serde(with = "serde_bytes")]
code: &'a [u8],
map: String,
}
macro_rules! make_test {
($name:ident, $val:expr) => {
#[js_function]
fn $name(ctx: CallContext) -> Result<JsUnknown> {
let value = $val;
ctx.env.to_js_value(&value)
}
};
}
make_test!(make_num_77, 77i32);
make_test!(make_num_32, 32u8);
make_test!(make_str_hello, "Hello World");
make_test!(make_num_array, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
make_test!(
make_obj,
AnObject {
a: 1,
b: vec![0.1f64, 1.1, 2.2, 3.3],
c: "Hi".into(),
}
);
make_test!(make_map, {
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
map
});
make_test!(make_object, {
AnObjectTwo {
a: 1,
b: vec![1, 2],
c: "abc".into(),
d: Some(false),
e: None,
f: Inner,
g: Inner2(9, false, "efg".into()),
h: '🤷',
i: TypeEnum::Empty,
j: TypeEnum::Tuple(27, "hij".into()),
k: TypeEnum::Struct {
a: 128,
b: vec![9, 8, 7],
},
l: "jkl".into(),
m: vec![0, 1, 2, 3, 4],
o: TypeEnum::Value(vec!['z', 'y', 'x']),
p: vec![1., 2., 3.5],
q: 9998881288248882845242411222333,
r: -3332323888900001232323022221345,
}
});
const NUMBER_BYTES: &[u8] = &[255u8, 254, 253];
make_test!(make_buff, { serde_bytes::Bytes::new(NUMBER_BYTES) });
make_test!(make_bytes_struct, {
BytesObject {
code: &[0, 1, 2, 3],
map: "source map".to_owned(),
}
});
macro_rules! make_expect {
($name:ident, $val:expr, $val_type:ty) => {
#[js_function(1)]
fn $name(ctx: CallContext) -> Result<JsUndefined> {
let value = $val;
let arg0 = ctx.get::<JsUnknown>(0)?;
let de_serialized: $val_type = ctx.env.from_js_value(arg0)?;
assert_eq!(value, de_serialized);
ctx.env.get_undefined()
}
};
}
make_expect!(expect_hello_world, "hello world", String);
make_expect!(
expect_obj,
AnObjectTwo {
a: 1,
b: vec![1, 2],
c: "abc".into(),
d: Some(false),
e: None,
f: Inner,
g: Inner2(9, false, "efg".into()),
h: '🤷',
i: TypeEnum::Empty,
j: TypeEnum::Tuple(27, "hij".into()),
k: TypeEnum::Struct {
a: 128,
b: vec![9, 8, 7],
},
l: "jkl".into(),
m: vec![0, 1, 2, 3, 4],
o: TypeEnum::Value(vec!['z', 'y', 'x']),
p: vec![1., 2., 3.5],
q: 9998881288248882845242411222333,
r: -3332323888900001232323022221345,
},
AnObjectTwo
);
make_expect!(expect_num_array, vec![0, 1, 2, 3], Vec<i32>);
make_expect!(
expect_buffer,
serde_bytes::ByteBuf::from(vec![252u8, 251, 250]),
serde_bytes::ByteBuf
);
#[js_function(1)]
fn roundtrip_object(ctx: CallContext) -> Result<JsUnknown> {
let arg0 = ctx.get::<JsObject>(0)?;
let de_serialized: AnObjectTwo = ctx.env.from_js_value(arg0)?;
ctx.env.to_js_value(&de_serialized)
}
#[js_function(1)]
fn from_json_string(ctx: CallContext) -> Result<JsUnknown> {
let arg0 = ctx.get::<JsString>(0)?.into_utf8()?;
let de_serialized: AnObject = from_str(arg0.as_str()?)?;
ctx.env.to_js_value(&de_serialized)
}
#[js_function(1)]
fn json_to_string(ctx: CallContext) -> Result<JsString> {
let arg0 = ctx.get::<JsObject>(0)?;
let de_serialized: AnObject = ctx.env.from_js_value(arg0)?;
let json_string = to_string(&de_serialized)?;
ctx.env.create_string_from_std(json_string)
}
pub fn register_js(exports: &mut JsObject) -> Result<()> {
exports.create_named_method("make_num_77", make_num_77)?;
exports.create_named_method("make_num_32", make_num_32)?;
exports.create_named_method("make_str_hello", make_str_hello)?;
exports.create_named_method("make_num_array", make_num_array)?;
exports.create_named_method("make_buff", make_buff)?;
exports.create_named_method("make_obj", make_obj)?;
exports.create_named_method("make_object", make_object)?;
exports.create_named_method("make_map", make_map)?;
exports.create_named_method("make_bytes_struct", make_bytes_struct)?;
exports.create_named_method("expect_hello_world", expect_hello_world)?;
exports.create_named_method("expect_obj", expect_obj)?;
exports.create_named_method("expect_num_array", expect_num_array)?;
exports.create_named_method("expect_buffer", expect_buffer)?;
exports.create_named_method("roundtrip_object", roundtrip_object)?;
exports.create_named_method("from_json_string", from_json_string)?;
exports.create_named_method("json_to_string", json_to_string)?;
Ok(())
}