#![cfg_attr(not(test), no_std)]
extern crate alloc;
use rquickjs::{Ctx, Value};
use serde::Serialize;
use serde::de::DeserializeOwned;
#[doc(inline)]
pub use crate::de::Deserializer;
#[doc(inline)]
pub use crate::err::{Error, Result};
#[doc(inline)]
pub use crate::ser::Serializer;
pub mod de;
pub mod err;
pub mod ser;
#[cfg(test)]
mod test;
mod utils;
pub const MAX_SAFE_INTEGER: i64 = 2_i64.pow(53) - 1;
pub const MIN_SAFE_INTEGER: i64 = -MAX_SAFE_INTEGER;
#[inline]
pub fn to_value<T>(context: Ctx<'_>, value: T) -> Result<Value<'_>>
where
T: Serialize,
{
let mut serializer = Serializer::from_context(context)?;
value.serialize(&mut serializer)?;
Ok(serializer.value)
}
#[inline]
pub fn from_value<T>(value: Value) -> Result<T>
where
T: DeserializeOwned,
{
let mut deserializer = Deserializer::from(value);
T::deserialize(&mut deserializer)
}
pub fn from_value_strict<T>(value: Value) -> Result<T>
where
T: DeserializeOwned,
{
let mut deserializer = Deserializer::from(value).with_strict();
T::deserialize(&mut deserializer)
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use quickcheck::quickcheck;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use crate::de::Deserializer as ValueDeserializer;
use crate::err::Result;
use crate::ser::Serializer as ValueSerializer;
use crate::test::Runtime;
use crate::{MAX_SAFE_INTEGER, MIN_SAFE_INTEGER};
quickcheck! {
fn test_str(expected: String) -> Result<bool> {
let actual = do_roundtrip::<_, String>(&expected);
Ok(expected == actual)
}
fn test_u8(expected: u8) -> Result<bool> {
let actual = do_roundtrip::<_, u8>(&expected);
Ok(expected == actual)
}
fn test_u16(expected: u16) -> Result<bool> {
let actual = do_roundtrip::<_, u16>(&expected);
Ok(expected == actual)
}
fn test_f32(expected: f32) -> quickcheck::TestResult {
if expected.is_nan() {
return quickcheck::TestResult::discard();
}
let actual = do_roundtrip::<_, f32>(&expected);
quickcheck::TestResult::from_bool(expected == actual)
}
fn test_i32(expected: i32) -> Result<bool> {
let actual = do_roundtrip::<_, i32>(&expected);
Ok(expected == actual)
}
fn test_i64(expected: i64) -> Result<bool> {
if (MIN_SAFE_INTEGER..=MAX_SAFE_INTEGER).contains(&expected) {
let actual = do_roundtrip::<_, i64>(&expected);
Ok(expected == actual)
} else {
let expected_f64 = expected as f64;
let actual = do_roundtrip::<_, f64>(&expected_f64);
Ok(expected_f64 == actual)
}
}
fn test_u32(expected: u32) -> Result<bool> {
let actual = do_roundtrip::<_, u32>(&expected);
Ok(expected == actual)
}
fn test_u64(expected: u64) -> Result<bool> {
if expected <= MAX_SAFE_INTEGER as u64 {
let actual = do_roundtrip::<_, u64>(&expected);
Ok(expected == actual)
} else {
let expected_f64 = expected as f64;
let actual = do_roundtrip::<_, f64>(&expected_f64);
Ok(expected_f64 == actual)
}
}
fn test_bool(expected: bool) -> Result<bool> {
let actual = do_roundtrip::<_, bool>(&expected);
Ok(expected == actual)
}
}
#[test]
fn test_map() {
let mut expected = BTreeMap::<String, String>::new();
expected.insert("foo".to_string(), "bar".to_string());
expected.insert("hello".to_string(), "world".to_string());
let actual = do_roundtrip::<_, BTreeMap<String, String>>(&expected);
assert_eq!(expected, actual);
}
#[test]
fn test_struct_into_map() {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct MyObject {
foo: String,
bar: u32,
}
let expected = MyObject {
foo: "hello".to_string(),
bar: 1337,
};
let actual = do_roundtrip::<_, MyObject>(&expected);
assert_eq!(expected, actual);
}
#[test]
fn test_nested_maps() {
let mut expected = BTreeMap::<String, BTreeMap<String, String>>::new();
let mut a = BTreeMap::new();
a.insert("foo".to_string(), "bar".to_string());
a.insert("hello".to_string(), "world".to_string());
let mut b = BTreeMap::new();
b.insert("toto".to_string(), "titi".to_string());
expected.insert("aaa".to_string(), a);
expected.insert("bbb".to_string(), b);
let actual = do_roundtrip::<_, BTreeMap<String, BTreeMap<String, String>>>(&expected);
assert_eq!(expected, actual);
}
#[test]
fn test_nested_structs_into_maps() {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct MyObjectB {
toto: String,
titi: i32,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct MyObjectA {
foo: String,
bar: u32,
b: MyObjectB,
}
let expected = MyObjectA {
foo: "hello".to_string(),
bar: 1337,
b: MyObjectB {
toto: "world".to_string(),
titi: -42,
},
};
let actual = do_roundtrip::<_, MyObjectA>(&expected);
assert_eq!(expected, actual);
}
#[test]
fn test_sequence() {
let expected = vec!["hello".to_string(), "world".to_string()];
let actual = do_roundtrip::<_, Vec<String>>(&expected);
assert_eq!(expected, actual);
}
#[test]
fn test_nested_sequences() {
let mut expected = Vec::new();
let a = vec!["foo".to_string(), "bar".to_string()];
let b = vec!["toto".to_string(), "tata".to_string()];
expected.push(a);
expected.push(b);
let actual = do_roundtrip::<_, Vec<Vec<String>>>(&expected);
assert_eq!(expected, actual);
}
#[test]
fn test_sanity() {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct MyObject {
a: u8,
b: u16,
c: u32,
d: u64,
e: i8,
f: i16,
g: i32,
h: i64,
i: f32,
j: f64,
k: String,
l: bool,
m: BTreeMap<String, u32>,
n: Vec<u32>,
o: BTreeMap<String, BTreeMap<String, u32>>,
p: Vec<Vec<u32>>,
bb: MyObjectB,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct MyObjectB {
a: u32,
cc: MyObjectC,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct MyObjectC {
a: Vec<u32>,
b: BTreeMap<String, u32>,
}
let mut cc_b = BTreeMap::new();
cc_b.insert("a".to_string(), 123);
cc_b.insert("b".to_string(), 456);
let cc = MyObjectC {
a: vec![1337, 42],
b: cc_b,
};
let bb = MyObjectB { a: 789, cc };
let mut m = BTreeMap::new();
m.insert("a".to_string(), 123);
m.insert("b".to_string(), 456);
m.insert("c".to_string(), 789);
let mut oo = BTreeMap::new();
oo.insert("e".to_string(), 123);
let mut o = BTreeMap::new();
o.insert("d".to_string(), oo);
let expected = MyObject {
a: u8::MAX,
b: u16::MAX,
c: u32::MAX,
d: MAX_SAFE_INTEGER as u64,
e: i8::MAX,
f: i16::MAX,
g: i32::MAX,
h: MIN_SAFE_INTEGER,
i: f32::MAX,
j: f64::MAX,
k: "hello world".to_string(),
l: true,
m,
n: vec![1, 2, 3, 4, 5],
o,
p: vec![vec![1, 2], vec![3, 4, 5]],
bb,
};
let actual = do_roundtrip::<_, MyObject>(&expected);
assert_eq!(expected, actual);
}
fn do_roundtrip<E, A>(expected: &E) -> A
where
E: Serialize,
A: DeserializeOwned,
{
let rt = Runtime::default();
rt.context().with(|cx| {
let mut serializer = ValueSerializer::from_context(cx).unwrap();
expected.serialize(&mut serializer).unwrap();
let mut deserializer = ValueDeserializer::from(serializer.value);
A::deserialize(&mut deserializer).unwrap()
})
}
}