Skip to main content

rquickjs_serde/
lib.rs

1#![cfg_attr(not(test), no_std)]
2
3extern crate alloc;
4
5use rquickjs::{Ctx, Value};
6use serde::Serialize;
7use serde::de::DeserializeOwned;
8
9#[doc(inline)]
10pub use crate::de::Deserializer;
11#[doc(inline)]
12pub use crate::err::{Error, Result};
13#[doc(inline)]
14pub use crate::ser::Serializer;
15
16pub mod de;
17pub mod err;
18pub mod ser;
19#[cfg(test)]
20mod test;
21mod utils;
22
23// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER#description
24pub const MAX_SAFE_INTEGER: i64 = 2_i64.pow(53) - 1;
25
26// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER#description
27pub const MIN_SAFE_INTEGER: i64 = -MAX_SAFE_INTEGER;
28
29/// Convert a `T` into `rquickjs::Value`
30///
31/// # Example
32///
33/// ```
34/// use std::error::Error;
35///
36/// use serde::Serialize;
37/// use rquickjs::{Runtime, Context};
38///
39/// #[derive(Serialize)]
40/// struct User {
41///     fingerprint: String,
42///     location: String,
43/// }
44///
45/// fn serialize_to_value() -> Result<(), Box<dyn Error>> {
46///     let u = User {
47///         fingerprint: "0xF9BA143B95FF6D82".to_owned(),
48///         location: "Menlo Park, CA".to_owned(),
49///     };
50///
51///     let rt = Runtime::new().unwrap();
52///     let ctx = Context::full(&rt).unwrap();
53///
54///     ctx.with(|ctx| {
55///         let v = rquickjs_serde::to_value(ctx, u).unwrap();
56///         let obj = v.into_object().unwrap();
57///
58///         let fingerprint: String = obj.get("fingerprint").unwrap();
59///         assert_eq!(fingerprint, "0xF9BA143B95FF6D82");
60///
61///         let location: String = obj.get("location").unwrap();
62///         assert_eq!(location, "Menlo Park, CA");
63///     });
64///
65///     Ok(())
66/// }
67/// #
68/// # serialize_to_value().unwrap();
69/// ```
70#[inline]
71pub fn to_value<T>(context: Ctx<'_>, value: T) -> Result<Value<'_>>
72where
73    T: Serialize,
74{
75    let mut serializer = Serializer::from_context(context)?;
76    value.serialize(&mut serializer)
77}
78
79/// Interpret a `rquickjs::Value` as an instance of type `T`.
80///
81/// # Example
82///
83/// ```
84/// use std::error::Error;
85///
86/// use serde::Deserialize;
87/// use rquickjs::{Runtime, Context, Value};
88///
89/// #[derive(Deserialize, Debug)]
90/// struct User {
91///     fingerprint: String,
92///     location: String,
93/// }
94///
95/// fn deserialize_from_value() -> Result<(), Box<dyn Error>> {
96///     let rt = Runtime::new().unwrap();
97///     let ctx = Context::full(&rt).unwrap();
98///
99///     let v = ctx.with(|ctx| {
100///          ctx.eval::<Value<'_>, _>("var a = {fingerprint: '0xF9BA143B95FF6D82', location: 'Menlo Park, CA'};").unwrap();
101///          let val = ctx.globals().get("a").unwrap();
102///          let u: User = rquickjs_serde::from_value(val).unwrap();
103///          u
104///     });
105///
106///     assert_eq!(v.fingerprint, "0xF9BA143B95FF6D82");
107///     assert_eq!(v.location, "Menlo Park, CA");
108///
109///     Ok(())
110/// }
111/// #
112/// # deserialize_from_value().unwrap();
113/// ```
114///
115/// # Errors
116///
117/// This conversion can fail if the structure of the Value does not match the
118/// structure expected by `T`, for example if `T` is a struct type but the Value
119/// contains something other than a JS Object. It can also fail if the structure
120/// is correct but `T`'s implementation of `Deserialize` decides that something
121/// is wrong with the data, for example required struct fields are missing from
122/// the JS Object or some number is too big to fit in the expected primitive
123/// type.
124#[inline]
125pub fn from_value<T>(value: Value) -> Result<T>
126where
127    T: DeserializeOwned,
128{
129    let mut deserializer = Deserializer::from(value);
130    T::deserialize(&mut deserializer)
131}
132
133/// Interpret a `rquickjs::Value` as an instance of type `T` in strict mode.
134///
135/// Strict mode will stick to the behaviour of JSON.stringify.
136///
137/// See [from_value] for more information about the deserialization.
138pub fn from_value_strict<T>(value: Value) -> Result<T>
139where
140    T: DeserializeOwned,
141{
142    let mut deserializer = Deserializer::from(value).with_strict();
143    T::deserialize(&mut deserializer)
144}
145
146#[cfg(test)]
147mod tests {
148    use std::collections::BTreeMap;
149
150    use quickcheck::quickcheck;
151    use serde::de::DeserializeOwned;
152    use serde::{Deserialize, Serialize};
153
154    use crate::de::Deserializer as ValueDeserializer;
155    use crate::err::Result;
156    use crate::ser::Serializer as ValueSerializer;
157    use crate::test::Runtime;
158    use crate::{MAX_SAFE_INTEGER, MIN_SAFE_INTEGER};
159
160    quickcheck! {
161        fn test_str(expected: String) -> Result<bool> {
162            let actual = do_roundtrip::<_, String>(&expected);
163            Ok(expected == actual)
164        }
165
166        fn test_u8(expected: u8) -> Result<bool> {
167            let actual = do_roundtrip::<_, u8>(&expected);
168            Ok(expected == actual)
169        }
170
171        fn test_u16(expected: u16) -> Result<bool> {
172            let actual = do_roundtrip::<_, u16>(&expected);
173            Ok(expected == actual)
174        }
175
176        fn test_f32(expected: f32) -> quickcheck::TestResult {
177            if expected.is_nan() {
178                return quickcheck::TestResult::discard();
179            }
180
181            let actual = do_roundtrip::<_, f32>(&expected);
182            quickcheck::TestResult::from_bool(expected == actual)
183        }
184
185        fn test_i32(expected: i32) -> Result<bool> {
186            let actual = do_roundtrip::<_, i32>(&expected);
187            Ok(expected == actual)
188        }
189
190        // This test is not representative of what is happening in the real world. Since we are transcoding
191        // from msgpack, only values greather than or equal to u32::MAX would be serialized as `BigInt`. Any other values would
192        // be serialized as a `number`.
193        //
194        // See https://github.com/3Hren/msgpack-rust/blob/aa3c4a77b2b901fe73a555c615b92773b40905fc/rmp/src/encode/sint.rs#L170.
195        //
196        // This test works here since we are explicitly calling serialize_i64 and deserialize_i64.
197        fn test_i64(expected: i64) -> Result<bool> {
198            if (MIN_SAFE_INTEGER..=MAX_SAFE_INTEGER).contains(&expected) {
199                let actual = do_roundtrip::<_, i64>(&expected);
200                Ok(expected == actual)
201            } else {
202                let expected_f64 = expected as f64;
203                let actual = do_roundtrip::<_, f64>(&expected_f64);
204                Ok(expected_f64 == actual)
205            }
206        }
207
208        fn test_u32(expected: u32) -> Result<bool> {
209            let actual = do_roundtrip::<_, u32>(&expected);
210            Ok(expected == actual)
211        }
212
213        // This test is not representative of what is happening in the real world. Since we are transcoding
214        // from msgpack, only values larger than i64::MAX would be serialized as BigInt. Any other values would
215        // be serialized as a number.
216        //
217        // See https://github.com/3Hren/msgpack-rust/blob/aa3c4a77b2b901fe73a555c615b92773b40905fc/rmp/src/encode/sint.rs#L170.
218        //
219        // This test works here since we are explicitly calling serialize_u64 and deserialize_u64.
220        fn test_u64(expected: u64) -> Result<bool> {
221            if expected <= MAX_SAFE_INTEGER as u64 {
222                let actual = do_roundtrip::<_, u64>(&expected);
223                Ok(expected == actual)
224            } else {
225                let expected_f64 = expected as f64;
226                let actual = do_roundtrip::<_, f64>(&expected_f64);
227                Ok(expected_f64 == actual)
228            }
229        }
230
231        fn test_bool(expected: bool) -> Result<bool> {
232            let actual = do_roundtrip::<_, bool>(&expected);
233            Ok(expected == actual)
234        }
235    }
236
237    #[test]
238    fn test_map() {
239        let mut expected = BTreeMap::<String, String>::new();
240        expected.insert("foo".to_string(), "bar".to_string());
241        expected.insert("hello".to_string(), "world".to_string());
242
243        let actual = do_roundtrip::<_, BTreeMap<String, String>>(&expected);
244
245        assert_eq!(expected, actual);
246    }
247
248    #[test]
249    fn test_struct_into_map() {
250        #[derive(Debug, Serialize, Deserialize, PartialEq)]
251        struct MyObject {
252            foo: String,
253            bar: u32,
254        }
255        let expected = MyObject {
256            foo: "hello".to_string(),
257            bar: 1337,
258        };
259
260        let actual = do_roundtrip::<_, MyObject>(&expected);
261
262        assert_eq!(expected, actual);
263    }
264
265    #[test]
266    fn test_nested_maps() {
267        let mut expected = BTreeMap::<String, BTreeMap<String, String>>::new();
268        let mut a = BTreeMap::new();
269        a.insert("foo".to_string(), "bar".to_string());
270        a.insert("hello".to_string(), "world".to_string());
271        let mut b = BTreeMap::new();
272        b.insert("toto".to_string(), "titi".to_string());
273        expected.insert("aaa".to_string(), a);
274        expected.insert("bbb".to_string(), b);
275
276        let actual = do_roundtrip::<_, BTreeMap<String, BTreeMap<String, String>>>(&expected);
277
278        assert_eq!(expected, actual);
279    }
280
281    #[test]
282    fn test_nested_structs_into_maps() {
283        #[derive(Debug, Serialize, Deserialize, PartialEq)]
284        struct MyObjectB {
285            toto: String,
286            titi: i32,
287        }
288
289        #[derive(Debug, Serialize, Deserialize, PartialEq)]
290        struct MyObjectA {
291            foo: String,
292            bar: u32,
293            b: MyObjectB,
294        }
295        let expected = MyObjectA {
296            foo: "hello".to_string(),
297            bar: 1337,
298            b: MyObjectB {
299                toto: "world".to_string(),
300                titi: -42,
301            },
302        };
303
304        let actual = do_roundtrip::<_, MyObjectA>(&expected);
305
306        assert_eq!(expected, actual);
307    }
308
309    #[test]
310    fn test_sequence() {
311        let expected = vec!["hello".to_string(), "world".to_string()];
312
313        let actual = do_roundtrip::<_, Vec<String>>(&expected);
314
315        assert_eq!(expected, actual);
316    }
317
318    #[test]
319    fn test_nested_sequences() {
320        let mut expected = Vec::new();
321        let a = vec!["foo".to_string(), "bar".to_string()];
322        let b = vec!["toto".to_string(), "tata".to_string()];
323        expected.push(a);
324        expected.push(b);
325
326        let actual = do_roundtrip::<_, Vec<Vec<String>>>(&expected);
327
328        assert_eq!(expected, actual);
329    }
330
331    #[test]
332    fn test_sanity() {
333        #[derive(Debug, Serialize, Deserialize, PartialEq)]
334        struct MyObject {
335            a: u8,
336            b: u16,
337            c: u32,
338            d: u64,
339            e: i8,
340            f: i16,
341            g: i32,
342            h: i64,
343            i: f32,
344            j: f64,
345            k: String,
346            l: bool,
347            m: BTreeMap<String, u32>,
348            n: Vec<u32>,
349            o: BTreeMap<String, BTreeMap<String, u32>>,
350            p: Vec<Vec<u32>>,
351            bb: MyObjectB,
352        }
353
354        #[derive(Debug, Serialize, Deserialize, PartialEq)]
355        struct MyObjectB {
356            a: u32,
357            cc: MyObjectC,
358        }
359
360        #[derive(Debug, Serialize, Deserialize, PartialEq)]
361        struct MyObjectC {
362            a: Vec<u32>,
363            b: BTreeMap<String, u32>,
364        }
365
366        let mut cc_b = BTreeMap::new();
367        cc_b.insert("a".to_string(), 123);
368        cc_b.insert("b".to_string(), 456);
369        let cc = MyObjectC {
370            a: vec![1337, 42],
371            b: cc_b,
372        };
373
374        let bb = MyObjectB { a: 789, cc };
375
376        let mut m = BTreeMap::new();
377        m.insert("a".to_string(), 123);
378        m.insert("b".to_string(), 456);
379        m.insert("c".to_string(), 789);
380
381        let mut oo = BTreeMap::new();
382        oo.insert("e".to_string(), 123);
383
384        let mut o = BTreeMap::new();
385        o.insert("d".to_string(), oo);
386
387        let expected = MyObject {
388            a: u8::MAX,
389            b: u16::MAX,
390            c: u32::MAX,
391            d: MAX_SAFE_INTEGER as u64,
392            e: i8::MAX,
393            f: i16::MAX,
394            g: i32::MAX,
395            h: MIN_SAFE_INTEGER,
396            i: f32::MAX,
397            j: f64::MAX,
398            k: "hello world".to_string(),
399            l: true,
400            m,
401            n: vec![1, 2, 3, 4, 5],
402            o,
403            p: vec![vec![1, 2], vec![3, 4, 5]],
404            bb,
405        };
406
407        let actual = do_roundtrip::<_, MyObject>(&expected);
408
409        assert_eq!(expected, actual);
410    }
411
412    fn do_roundtrip<E, A>(expected: &E) -> A
413    where
414        E: Serialize,
415        A: DeserializeOwned,
416    {
417        let rt = Runtime::default();
418        rt.context().with(|cx| {
419            let mut serializer = ValueSerializer::from_context(cx).unwrap();
420            let value = expected.serialize(&mut serializer).unwrap();
421            let mut deserializer = ValueDeserializer::from(value);
422            A::deserialize(&mut deserializer).unwrap()
423        })
424    }
425}