Skip to main content

kg_js/
de.rs

1use super::*;
2use serde::de::*;
3
4
5impl<'de, T: Deserialize<'de>> ReadJs for T {
6    fn read_js(ctx: &DukContext, obj_index: i32) -> Result<Self, JsError> {
7        Self::deserialize(JsEngineDeserializer::new(ctx, obj_index))
8    }
9}
10
11pub struct JsEngineDeserializer<'a> {
12    ctx: &'a DukContext,
13    index: i32,
14    len: usize,
15}
16
17impl <'a> JsEngineDeserializer<'a> {
18    pub fn new(ctx: &'a DukContext, index: i32) -> Self {
19        Self { ctx, index, len: 0 }
20    }
21}
22
23impl<'de, 'a> Deserializer<'de> for JsEngineDeserializer<'a> {
24    type Error = JsError;
25
26    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
27        use super::DukType::*;
28
29        match self.ctx.get_type(self.index) {
30            DUK_TYPE_UNDEFINED | DUK_TYPE_NULL => visitor.visit_none(),
31            DUK_TYPE_BOOLEAN => visitor.visit_bool(self.ctx.get_boolean(self.index)),
32            DUK_TYPE_NUMBER => {
33                let n = self.ctx.get_number(self.index);
34                if n.is_finite() && (n.trunc() - n).abs() < f64::EPSILON {
35                    visitor.visit_i64(n as i64)
36                } else {
37                    visitor.visit_f64(n)
38                }
39            }
40            DUK_TYPE_STRING => visitor.visit_str(&self.ctx.get_string(self.index)),
41            DUK_TYPE_BUFFER => visitor.visit_bytes(self.ctx.get_buffer(self.index)),
42            DUK_TYPE_OBJECT => {
43                if self.ctx.is_array(self.index) {
44                    let len = self.ctx.get_length( self.index);
45                    self.ctx.enum_indices(self.index);
46                    let res = visitor.visit_seq(JsEngineDeserializer { ctx: self.ctx, index: -1, len });
47                    self.ctx.pop();
48                    res
49                } else if self.ctx.is_pure_object(self.index) {
50                    self.ctx.enum_keys(self.index);
51                    let res = visitor.visit_map(JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 });
52                    self.ctx.pop();
53                    res
54                } else {
55                    return Err(JsError::from(format!("Unimplemented javascript object type"))); //FIXME (jc)
56                }
57            }
58            _ => return Err(JsError::from(format!("Unimplemented javascript object type"))) //FIXME (jc),
59        }
60    }
61
62    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
63        self.deserialize_any(visitor)
64    }
65
66    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
67        self.deserialize_any(visitor)
68    }
69
70    fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
71        self.deserialize_any(visitor)
72    }
73
74    fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
75        self.deserialize_any(visitor)
76    }
77
78    fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
79        self.deserialize_any(visitor)
80    }
81
82    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
83        self.deserialize_any(visitor)
84    }
85
86    fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
87        self.deserialize_any(visitor)
88    }
89
90    fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
91        self.deserialize_any(visitor)
92    }
93
94    fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
95        self.deserialize_any(visitor)
96    }
97
98    fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
99        self.deserialize_any(visitor)
100    }
101
102    fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
103        self.deserialize_any(visitor)
104    }
105
106    fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
107        self.deserialize_any(visitor)
108    }
109
110    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
111        self.deserialize_any(visitor)
112    }
113
114    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
115        self.deserialize_any(visitor)
116    }
117
118    fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
119        self.deserialize_any(visitor)
120    }
121
122    fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
123        self.deserialize_any(visitor)
124    }
125
126    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
127        use super::DukType::{DUK_TYPE_NULL, DUK_TYPE_UNDEFINED};
128
129        match self.ctx.get_type(self.index) {
130            DUK_TYPE_UNDEFINED | DUK_TYPE_NULL => visitor.visit_none(),
131            _ => visitor.visit_some(self)
132        }
133    }
134
135    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
136        visitor.visit_unit()
137    }
138
139    fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
140        self.deserialize_any(visitor)
141    }
142
143    fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
144        self.deserialize_any(visitor)
145    }
146
147    fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
148        self.deserialize_any(visitor)
149    }
150
151    fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
152        self.deserialize_any(visitor)
153    }
154
155    fn deserialize_tuple_struct<V>(self, _name: &'static str, _len: usize, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
156        self.deserialize_any(visitor)
157    }
158
159    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
160        self.deserialize_any(visitor)
161    }
162
163    fn deserialize_struct<V>(self, _name: &'static str, _fields: &'static [&'static str], visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
164        self.deserialize_any(visitor)
165    }
166
167    fn deserialize_enum<V>(self, _name: &'static str, _variants: &'static [&'static str], visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
168        use super::DukType::*;
169
170        match self.ctx.get_type(self.index) {
171            // Unit variant, serialized by `serialize_unit_variant` as a bare string.
172            DUK_TYPE_STRING => {
173                visitor.visit_enum(self.ctx.get_string(self.index).into_deserializer())
174            }
175            // Newtype/tuple/struct variant, serialized as a single-key object `{ Variant: payload }`.
176            DUK_TYPE_OBJECT if self.ctx.is_pure_object(self.index) => {
177                self.ctx.enum_keys(self.index);
178                if !self.ctx.next(-1) {
179                    self.ctx.pop();
180                    return Err(JsError::from(String::from(
181                        "expected an enum object with a single variant key, got an empty object",
182                    )));
183                }
184                // stack: [.., enumerator, key, value]
185                if self.ctx.next(-3) {
186                    // stack: [.., enumerator, key, value, extra_key, extra_value]
187                    self.ctx.pop_n(2); // extra_key + extra_value
188                    self.ctx.pop_n(2); // key + value
189                    self.ctx.pop();    // enumerator
190                    return Err(JsError::from(String::from(
191                        "expected an enum object with a single variant key, got multiple keys",
192                    )));
193                }
194                let res = visitor.visit_enum(JsEnumAccess { ctx: self.ctx });
195                self.ctx.pop_n(2); // key + value
196                self.ctx.pop();    // enumerator
197                res
198            }
199            _ => Err(JsError::from(String::from(
200                "expected a string or an object to deserialize an enum",
201            ))),
202        }
203    }
204
205    fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
206        self.deserialize_any(visitor)
207    }
208
209    fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
210        self.deserialize_any(visitor)
211    }
212}
213
214// `JsEnumAccess` pushes and pops nothing itself: `deserialize_enum` owns the push/pop
215// of the enumerator and the single key/value pair around the `visit_enum` call above.
216struct JsEnumAccess<'a> {
217    ctx: &'a DukContext,
218}
219
220impl<'de, 'a> EnumAccess<'de> for JsEnumAccess<'a> {
221    type Error = JsError;
222    type Variant = Self;
223
224    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
225        where V: DeserializeSeed<'de>
226    {
227        // the variant name is the key, at -2
228        let variant = seed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -2, len: 0 })?;
229        Ok((variant, self))
230    }
231}
232
233impl<'de, 'a> VariantAccess<'de> for JsEnumAccess<'a> {
234    type Error = JsError;
235
236    fn unit_variant(self) -> Result<(), Self::Error> {
237        Ok(())
238    }
239
240    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
241        where T: DeserializeSeed<'de>
242    {
243        // the payload is the value, at -1
244        seed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 })
245    }
246
247    fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
248        where V: Visitor<'de>
249    {
250        JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 }.deserialize_seq(visitor)
251    }
252
253    fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V)
254        -> Result<V::Value, Self::Error> where V: Visitor<'de>
255    {
256        JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 }.deserialize_map(visitor)
257    }
258}
259
260impl<'de, 'a> MapAccess<'de> for JsEngineDeserializer<'a> {
261    type Error = JsError;
262
263    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error> where K: DeserializeSeed<'de> {
264        if self.ctx.next(-1) {
265            Ok(Some(seed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -2, len: 0 })?))
266        } else {
267            Ok(None)
268        }
269    }
270
271    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error> where V: DeserializeSeed<'de> {
272        let res = seed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 });
273        self.ctx.pop_n(2);
274        res
275    }
276
277    fn next_entry_seed<K, V>(&mut self, kseed: K, vseed: V) -> Result<Option<(K::Value, V::Value)>, Self::Error> where K: DeserializeSeed<'de>, V: DeserializeSeed<'de> {
278        if self.ctx.next(-1) {
279            let k = kseed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -2, len: 0 })?;
280            let v = vseed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 })?;
281            self.ctx.pop_n(2);
282            Ok(Some((k, v)))
283        } else {
284            Ok(None)
285        }
286    }
287}
288
289impl<'de, 'a> SeqAccess<'de> for JsEngineDeserializer<'a> {
290    type Error = JsError;
291
292    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error> where T: DeserializeSeed<'de> {
293        if self.ctx.next(-1) {
294            let v = seed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 })?;
295            self.ctx.pop_n(2);
296            Ok(Some(v))
297        } else {
298            Ok(None)
299        }
300    }
301
302    fn size_hint(&self) -> Option<usize> {
303        Some(self.len)
304    }
305}
306
307
308#[cfg(test)]
309mod tests {
310    use super::*;
311    use smart_default::SmartDefault;
312    use serde::{Serialize, Deserialize};
313
314    fn deserialize<'a, T: std::fmt::Debug + Serialize + Deserialize<'a> + Default>(value: &T) {
315        let e = JsEngine::new().unwrap();
316        e.write(value).unwrap_or_else(|err| {
317            panic!("{}", err);
318        });
319        e.put_global_string("value");
320        e.get_global_string("value");
321        let val: T = e.read_top().unwrap_or_else(|err| {
322            panic!("{}", err);
323        });
324        assert_eq!(format!("{:?}", value), format!("{:?}", val));
325    }
326
327    fn deserialize_expr<'a, T: std::fmt::Debug + Deserialize<'a>>(expr: &str) -> T {
328        let e = JsEngine::new().unwrap();
329        e.eval(expr).unwrap();
330        e.get_global_string("value");
331        let val: T = e.read_top().unwrap_or_else(|err| {
332            panic!("{}", err);
333        });
334        val
335    }
336
337    fn test_deserialize<'a, T: std::fmt::Debug + Serialize + Deserialize<'a> + Default>(value: &T) {
338        deserialize(value);
339    }
340
341    #[derive(Debug, SmartDefault, Serialize, Deserialize)]
342    struct TestStruct {
343        #[default = "string value"]
344        string_field: String,
345        #[default = 'A']
346        char_field: char,
347        #[default = 1]
348        i8_field: i8,
349        #[default(_code = "vec![1.0,2.0,3.0,7.5]")]
350        arr_field: Vec<f64>,
351        optional1: Option<f64>,
352        optional2: Option<f64>,
353        unit: ()
354    }
355
356    #[test]
357    fn read_struct() {
358        let mut p = TestStruct::default();
359        p.char_field = 'B';
360        p.i8_field = 44;
361        p.optional1 = Some(3.14);
362        test_deserialize(&p);
363    }
364
365    #[test]
366    fn deserialize_zero() {
367        #[derive(Debug, Deserialize)]
368        struct TestStruct {
369            float_field: f64,
370            int_field: i64,
371        }
372
373        //language=JavaScript
374        let val: TestStruct = deserialize_expr(r#"
375    value = {
376        float_field: 0,
377        int_field: 0
378    };
379"#);
380        assert_eq!(val.float_field, 0.0);
381        assert_eq!(val.int_field, 0);
382    }
383
384    #[test]
385    fn deserialize_nan() {
386        #[derive(Debug, Deserialize)]
387        struct TestStruct {
388            float_field: f64,
389        }
390
391        //language=JavaScript
392        let val: TestStruct = deserialize_expr(r#"
393    value = {
394        float_field: NaN
395    };
396"#);
397        assert!(val.float_field.is_nan());
398    }
399
400    #[test]
401    fn deserialize_unit() {
402        //language=JavaScript
403        let _val: () = deserialize_expr(r#"
404    value = {
405        test: "asfads"
406    };
407"#);
408    }
409
410    #[derive(Debug, PartialEq, Serialize, Deserialize)]
411    #[serde(rename_all = "lowercase")]
412    enum HttpMethod { Get, Post, Delete }
413
414    #[derive(Debug, PartialEq, Serialize, Deserialize)]
415    enum Shape {
416        Empty,
417        Radius(f64),
418        Pair(i64, i64),
419        Rect { w: i64, h: i64 },
420    }
421
422    #[derive(Debug, PartialEq, Serialize, Deserialize)]
423    struct Req { url: String, method: HttpMethod }
424
425    #[test]
426    fn bare_unit_variant_from_string() {
427        let val: HttpMethod = deserialize_expr("value = 'post'");
428        assert_eq!(val, HttpMethod::Post);
429        let val: HttpMethod = deserialize_expr("value = 'get'");
430        assert_eq!(val, HttpMethod::Get);
431    }
432
433    #[test]
434    fn unit_variant_as_struct_field() {
435        //language=JavaScript
436        let val: Req = deserialize_expr(r#"
437    value = {
438        url: 'http://x',
439        method: 'delete'
440    };
441"#);
442        assert_eq!(val, Req { url: "http://x".to_string(), method: HttpMethod::Delete });
443    }
444
445    #[test]
446    fn unknown_variant_is_a_clean_error() {
447        let e = JsEngine::new().unwrap();
448        e.eval("value = 'teapot'").unwrap();
449        e.get_global_string("value");
450        let res: Result<HttpMethod, _> = e.read_top();
451        let err = res.unwrap_err();
452        assert!(err.to_string().contains("unknown variant"), "error was: {}", err);
453    }
454
455    #[test]
456    fn tagged_variants_from_js_objects() {
457        let val: Shape = deserialize_expr("value = ({Radius: 2.5})");
458        assert_eq!(val, Shape::Radius(2.5));
459
460        let val: Shape = deserialize_expr("value = ({Pair: [1,2]})");
461        assert_eq!(val, Shape::Pair(1, 2));
462
463        let val: Shape = deserialize_expr("value = ({Rect: {w:3,h:4}})");
464        assert_eq!(val, Shape::Rect { w: 3, h: 4 });
465    }
466
467    #[test]
468    fn empty_object_variant_is_a_clean_error() {
469        let e = JsEngine::new().unwrap();
470        e.eval("value = ({})").unwrap();
471        e.get_global_string("value");
472        let res: Result<Shape, _> = e.read_top();
473        let err = res.unwrap_err();
474        assert!(err.to_string().contains("empty object"), "error was: {}", err);
475    }
476
477    #[test]
478    fn multi_key_object_variant_is_a_clean_error() {
479        let e = JsEngine::new().unwrap();
480        e.eval("value = ({Radius: 2.5, Extra: 999})").unwrap();
481        e.get_global_string("value");
482        let res: Result<Shape, _> = e.read_top();
483        let err = res.unwrap_err();
484        assert!(err.to_string().contains("multiple keys"), "error was: {}", err);
485    }
486
487    #[test]
488    fn all_variant_shapes_round_trip() {
489        for shape in [
490            Shape::Empty,
491            Shape::Radius(2.5),
492            Shape::Pair(1, 2),
493            Shape::Rect { w: 3, h: 4 },
494        ] {
495            let e = JsEngine::new().unwrap();
496            e.write(&shape).unwrap();
497            e.put_global_string("value");
498            e.get_global_string("value");
499            let val: Shape = e.read_top().unwrap();
500            assert_eq!(shape, val);
501        }
502
503        let req = Req { url: "http://x".to_string(), method: HttpMethod::Post };
504        let e = JsEngine::new().unwrap();
505        e.write(&req).unwrap();
506        e.put_global_string("value");
507        e.get_global_string("value");
508        let val: Req = e.read_top().unwrap();
509        assert_eq!(req, val);
510    }
511}