rsonpath/engine/
serde.rs

1use crate::{
2    automaton::Automaton,
3    engine::{main::MainEngine, Compiler},
4};
5use serde::{
6    de::{self, Visitor},
7    ser::SerializeTuple,
8    Deserialize, Serialize,
9};
10
11#[derive(Debug, Serialize, Deserialize)]
12enum BinaryVersion {
13    /// Placeholder for any version in the past, used for tests.
14    Past,
15    /// Introduced binary serialization in v0.9.4.
16    V1,
17}
18
19impl de::Expected for BinaryVersion {
20    fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
21        match *self {
22            Self::Past => write!(formatter, "Past"),
23            Self::V1 => write!(formatter, "v0.9.4"),
24        }
25    }
26}
27
28impl Serialize for MainEngine {
29    #[inline]
30    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
31    where
32        S: serde::Serializer,
33    {
34        let mut tuple_ser = serializer.serialize_tuple(2)?;
35        tuple_ser.serialize_element(&BinaryVersion::V1)?;
36        tuple_ser.serialize_element(&self.automaton())?;
37        tuple_ser.end()
38    }
39}
40
41impl<'de> Deserialize<'de> for MainEngine {
42    #[inline]
43    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
44    where
45        D: serde::Deserializer<'de>,
46    {
47        let automaton = deserializer.deserialize_tuple(2, EngineVisitor)?;
48        Ok(Self::from_compiled_query(automaton))
49    }
50}
51
52struct EngineVisitor;
53
54impl<'de> Visitor<'de> for EngineVisitor {
55    type Value = Automaton;
56
57    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
58        write!(formatter, "the binary version and the Automaton")
59    }
60
61    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
62    where
63        A: serde::de::SeqAccess<'de>,
64    {
65        let version = seq.next_element::<BinaryVersion>()?;
66        match version {
67            Some(BinaryVersion::V1) => (),
68            Some(v) => return Err(de::Error::custom(format!("binary version {:?} is incompatible", v))),
69            None => return Err(de::Error::missing_field("version")),
70        }
71        let automaton = seq.next_element::<Automaton>()?;
72        match automaton {
73            Some(a) => Ok(a),
74            None => Err(de::Error::missing_field("automaton")),
75        }
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use crate::{
83        automaton::Automaton,
84        engine::{Compiler, RsonpathEngine},
85    };
86    use serde::{ser::SerializeTuple, Serialize, Serializer};
87    use std::error::Error;
88
89    #[test]
90    fn automaton_round_trip() -> Result<(), Box<dyn Error>> {
91        let query_str = "$..phoneNumbers[*].number";
92        let query = rsonpath_syntax::parse(query_str)?;
93        let automaton = Automaton::new(&query)?;
94        let engine = RsonpathEngine::from_compiled_query(automaton.clone());
95
96        let json_string = serde_json::to_string(&engine)?;
97
98        let round_trip: RsonpathEngine = serde_json::from_str(&json_string)?;
99
100        assert_eq!(&automaton, round_trip.automaton());
101
102        Ok(())
103    }
104
105    #[test]
106    fn deserializing_from_older_version() -> Result<(), Box<dyn Error>> {
107        struct OldEngine {
108            automaton: Automaton,
109        }
110        impl Serialize for OldEngine {
111            #[inline]
112            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
113            where
114                S: Serializer,
115            {
116                let mut tuple_ser = serializer.serialize_tuple(2)?;
117                tuple_ser.serialize_element(&BinaryVersion::Past)?;
118                tuple_ser.serialize_element(&self.automaton)?;
119                tuple_ser.end()
120            }
121        }
122
123        let query_str = "$..phoneNumbers[*].number";
124        let query = rsonpath_syntax::parse(query_str)?;
125        let automaton = Automaton::new(&query)?;
126        let engine = OldEngine { automaton };
127
128        let json_string = serde_json::to_string(&engine)?;
129
130        match serde_json::from_str::<RsonpathEngine>(&json_string) {
131            Ok(_) => panic!("expected error"),
132            Err(e) => assert!(e.to_string().contains("binary version Past is incompatible")),
133        }
134
135        Ok(())
136    }
137
138    mod proptests {
139        use super::{Automaton, Compiler, RsonpathEngine};
140        use pretty_assertions::assert_eq;
141        use proptest::prelude::*;
142        use rsonpath_syntax_proptest::{ArbitraryJsonPathQuery, ArbitraryJsonPathQueryParams};
143
144        proptest! {
145            #[test]
146            fn main_engine_cbor_roundtrips(ArbitraryJsonPathQuery { parsed, .. } in prop::arbitrary::any_with::<ArbitraryJsonPathQuery>(
147                ArbitraryJsonPathQueryParams {
148                    only_rsonpath_supported_subset: true,
149                    ..Default::default()
150                }
151            )) {
152                use std::io;
153                struct ReadBuf<'a> {
154                    buf: &'a [u8],
155                    idx: usize,
156                }
157                impl<'a> io::Read for &mut ReadBuf<'a> {
158                    fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
159                        let len = std::cmp::min(self.buf.len() - self.idx, buf.len());
160                        buf.copy_from_slice(&self.buf[self.idx..self.idx + len]);
161                        self.idx += len;
162                        Ok(len)
163                    }
164                }
165
166                let automaton = Automaton::new(&parsed)?;
167                let engine = RsonpathEngine::from_compiled_query(automaton.clone());
168
169                let mut buf = vec![];
170                ciborium::into_writer(&engine, &mut buf)?;
171
172                let mut read = ReadBuf { buf: &buf, idx: 0 };
173                let engine_deser = ciborium::from_reader::<RsonpathEngine, _>(&mut read)?;
174
175                assert_eq!(&automaton, engine_deser.automaton());
176            }
177
178            #[test]
179            fn main_engine_json_roundtrips(ArbitraryJsonPathQuery { parsed, .. } in prop::arbitrary::any_with::<ArbitraryJsonPathQuery>(
180                ArbitraryJsonPathQueryParams {
181                    only_rsonpath_supported_subset: true,
182                    ..Default::default()
183                }
184            )) {
185                let automaton = Automaton::new(&parsed)?;
186                let engine = RsonpathEngine::from_compiled_query(automaton.clone());
187
188                let json_str = serde_json::to_string(&engine)?;
189                let engine_deser = serde_json::from_str::<RsonpathEngine>(&json_str)?;
190
191                assert_eq!(&automaton, engine_deser.automaton());
192            }
193
194            #[test]
195            fn main_engine_message_pack_roundtrips(ArbitraryJsonPathQuery { parsed, .. } in prop::arbitrary::any_with::<ArbitraryJsonPathQuery>(
196                ArbitraryJsonPathQueryParams {
197                    only_rsonpath_supported_subset: true,
198                    ..Default::default()
199                }
200            )) {
201                let automaton = Automaton::new(&parsed)?;
202                let engine = RsonpathEngine::from_compiled_query(automaton.clone());
203
204                let buf = rmp_serde::to_vec(&engine)?;
205                let engine_deser = rmp_serde::from_slice::<RsonpathEngine>(&buf)?;
206
207                assert_eq!(&automaton, engine_deser.automaton());
208            }
209        }
210    }
211}