rquickjs_serde/
lib.rs

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