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#[cfg(test)]
131mod tests {
132    use std::collections::BTreeMap;
133
134    use quickcheck::quickcheck;
135    use serde::de::DeserializeOwned;
136    use serde::{Deserialize, Serialize};
137
138    use crate::de::Deserializer as ValueDeserializer;
139    use crate::err::Result;
140    use crate::ser::Serializer as ValueSerializer;
141    use crate::test::Runtime;
142    use crate::{MAX_SAFE_INTEGER, MIN_SAFE_INTEGER};
143
144    quickcheck! {
145        fn test_str(expected: String) -> Result<bool> {
146            let actual = do_roundtrip::<_, String>(&expected);
147            Ok(expected == actual)
148        }
149
150        fn test_u8(expected: u8) -> Result<bool> {
151            let actual = do_roundtrip::<_, u8>(&expected);
152            Ok(expected == actual)
153        }
154
155        fn test_u16(expected: u16) -> Result<bool> {
156            let actual = do_roundtrip::<_, u16>(&expected);
157            Ok(expected == actual)
158        }
159
160        fn test_f32(expected: f32) -> quickcheck::TestResult {
161            if expected.is_nan() {
162                return quickcheck::TestResult::discard();
163            }
164
165            let actual = do_roundtrip::<_, f32>(&expected);
166            quickcheck::TestResult::from_bool(expected == actual)
167        }
168
169        fn test_i32(expected: i32) -> Result<bool> {
170            let actual = do_roundtrip::<_, i32>(&expected);
171            Ok(expected == actual)
172        }
173
174        // This test is not representative of what is happening in the real world. Since we are transcoding
175        // from msgpack, only values greather than or equal to u32::MAX would be serialized as `BigInt`. Any other values would
176        // be serialized as a `number`.
177        //
178        // See https://github.com/3Hren/msgpack-rust/blob/aa3c4a77b2b901fe73a555c615b92773b40905fc/rmp/src/encode/sint.rs#L170.
179        //
180        // This test works here since we are explicitly calling serialize_i64 and deserialize_i64.
181        fn test_i64(expected: i64) -> Result<bool> {
182            if (MIN_SAFE_INTEGER..=MAX_SAFE_INTEGER).contains(&expected) {
183                let actual = do_roundtrip::<_, i64>(&expected);
184                Ok(expected == actual)
185            } else {
186                let expected_f64 = expected as f64;
187                let actual = do_roundtrip::<_, f64>(&expected_f64);
188                Ok(expected_f64 == actual)
189            }
190        }
191
192        fn test_u32(expected: u32) -> Result<bool> {
193            let actual = do_roundtrip::<_, u32>(&expected);
194            Ok(expected == actual)
195        }
196
197        // This test is not representative of what is happening in the real world. Since we are transcoding
198        // from msgpack, only values larger than i64::MAX would be serialized as BigInt. Any other values would
199        // be serialized as a number.
200        //
201        // See https://github.com/3Hren/msgpack-rust/blob/aa3c4a77b2b901fe73a555c615b92773b40905fc/rmp/src/encode/sint.rs#L170.
202        //
203        // This test works here since we are explicitly calling serialize_u64 and deserialize_u64.
204        fn test_u64(expected: u64) -> Result<bool> {
205            if expected <= MAX_SAFE_INTEGER as u64 {
206                let actual = do_roundtrip::<_, u64>(&expected);
207                Ok(expected == actual)
208            } else {
209                let expected_f64 = expected as f64;
210                let actual = do_roundtrip::<_, f64>(&expected_f64);
211                Ok(expected_f64 == actual)
212            }
213        }
214
215        fn test_bool(expected: bool) -> Result<bool> {
216            let actual = do_roundtrip::<_, bool>(&expected);
217            Ok(expected == actual)
218        }
219    }
220
221    #[test]
222    fn test_map() {
223        let mut expected = BTreeMap::<String, String>::new();
224        expected.insert("foo".to_string(), "bar".to_string());
225        expected.insert("hello".to_string(), "world".to_string());
226
227        let actual = do_roundtrip::<_, BTreeMap<String, String>>(&expected);
228
229        assert_eq!(expected, actual);
230    }
231
232    #[test]
233    fn test_struct_into_map() {
234        #[derive(Debug, Serialize, Deserialize, PartialEq)]
235        struct MyObject {
236            foo: String,
237            bar: u32,
238        }
239        let expected = MyObject {
240            foo: "hello".to_string(),
241            bar: 1337,
242        };
243
244        let actual = do_roundtrip::<_, MyObject>(&expected);
245
246        assert_eq!(expected, actual);
247    }
248
249    #[test]
250    fn test_nested_maps() {
251        let mut expected = BTreeMap::<String, BTreeMap<String, String>>::new();
252        let mut a = BTreeMap::new();
253        a.insert("foo".to_string(), "bar".to_string());
254        a.insert("hello".to_string(), "world".to_string());
255        let mut b = BTreeMap::new();
256        b.insert("toto".to_string(), "titi".to_string());
257        expected.insert("aaa".to_string(), a);
258        expected.insert("bbb".to_string(), b);
259
260        let actual = do_roundtrip::<_, BTreeMap<String, BTreeMap<String, String>>>(&expected);
261
262        assert_eq!(expected, actual);
263    }
264
265    #[test]
266    fn test_nested_structs_into_maps() {
267        #[derive(Debug, Serialize, Deserialize, PartialEq)]
268        struct MyObjectB {
269            toto: String,
270            titi: i32,
271        }
272
273        #[derive(Debug, Serialize, Deserialize, PartialEq)]
274        struct MyObjectA {
275            foo: String,
276            bar: u32,
277            b: MyObjectB,
278        }
279        let expected = MyObjectA {
280            foo: "hello".to_string(),
281            bar: 1337,
282            b: MyObjectB {
283                toto: "world".to_string(),
284                titi: -42,
285            },
286        };
287
288        let actual = do_roundtrip::<_, MyObjectA>(&expected);
289
290        assert_eq!(expected, actual);
291    }
292
293    #[test]
294    fn test_sequence() {
295        let expected = vec!["hello".to_string(), "world".to_string()];
296
297        let actual = do_roundtrip::<_, Vec<String>>(&expected);
298
299        assert_eq!(expected, actual);
300    }
301
302    #[test]
303    fn test_nested_sequences() {
304        let mut expected = Vec::new();
305        let a = vec!["foo".to_string(), "bar".to_string()];
306        let b = vec!["toto".to_string(), "tata".to_string()];
307        expected.push(a);
308        expected.push(b);
309
310        let actual = do_roundtrip::<_, Vec<Vec<String>>>(&expected);
311
312        assert_eq!(expected, actual);
313    }
314
315    #[test]
316    fn test_sanity() {
317        #[derive(Debug, Serialize, Deserialize, PartialEq)]
318        struct MyObject {
319            a: u8,
320            b: u16,
321            c: u32,
322            d: u64,
323            e: i8,
324            f: i16,
325            g: i32,
326            h: i64,
327            i: f32,
328            j: f64,
329            k: String,
330            l: bool,
331            m: BTreeMap<String, u32>,
332            n: Vec<u32>,
333            o: BTreeMap<String, BTreeMap<String, u32>>,
334            p: Vec<Vec<u32>>,
335            bb: MyObjectB,
336        }
337
338        #[derive(Debug, Serialize, Deserialize, PartialEq)]
339        struct MyObjectB {
340            a: u32,
341            cc: MyObjectC,
342        }
343
344        #[derive(Debug, Serialize, Deserialize, PartialEq)]
345        struct MyObjectC {
346            a: Vec<u32>,
347            b: BTreeMap<String, u32>,
348        }
349
350        let mut cc_b = BTreeMap::new();
351        cc_b.insert("a".to_string(), 123);
352        cc_b.insert("b".to_string(), 456);
353        let cc = MyObjectC {
354            a: vec![1337, 42],
355            b: cc_b,
356        };
357
358        let bb = MyObjectB { a: 789, cc };
359
360        let mut m = BTreeMap::new();
361        m.insert("a".to_string(), 123);
362        m.insert("b".to_string(), 456);
363        m.insert("c".to_string(), 789);
364
365        let mut oo = BTreeMap::new();
366        oo.insert("e".to_string(), 123);
367
368        let mut o = BTreeMap::new();
369        o.insert("d".to_string(), oo);
370
371        let expected = MyObject {
372            a: u8::MAX,
373            b: u16::MAX,
374            c: u32::MAX,
375            d: MAX_SAFE_INTEGER as u64,
376            e: i8::MAX,
377            f: i16::MAX,
378            g: i32::MAX,
379            h: MIN_SAFE_INTEGER,
380            i: f32::MAX,
381            j: f64::MAX,
382            k: "hello world".to_string(),
383            l: true,
384            m,
385            n: vec![1, 2, 3, 4, 5],
386            o,
387            p: vec![vec![1, 2], vec![3, 4, 5]],
388            bb,
389        };
390
391        let actual = do_roundtrip::<_, MyObject>(&expected);
392
393        assert_eq!(expected, actual);
394    }
395
396    fn do_roundtrip<E, A>(expected: &E) -> A
397    where
398        E: Serialize,
399        A: DeserializeOwned,
400    {
401        let rt = Runtime::default();
402        rt.context().with(|cx| {
403            let mut serializer = ValueSerializer::from_context(cx).unwrap();
404            expected.serialize(&mut serializer).unwrap();
405            let mut deserializer = ValueDeserializer::from(serializer.value);
406            A::deserialize(&mut deserializer).unwrap()
407        })
408    }
409}