Skip to main content

rsonpath/engine/
serde.rs

1use crate::{
2    automaton::Automaton,
3    engine::{main::MainEngine, Compiler as _},
4};
5use serde::{
6    de::{self, Visitor},
7    ser::SerializeTuple as _,
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 {v:?} is incompatible"))),
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 = match Automaton::new(&parsed) {
167                    Ok(x) => Ok(x),
168                    Err(crate::automaton::error::CompilerError::QueryTooComplex(_)) => Err(TestCaseError::Reject("query too complex".into())),
169                    Err(e) => Err(e.into()),
170                }?;
171                let engine = RsonpathEngine::from_compiled_query(automaton.clone());
172
173                let mut buf = vec![];
174                ciborium::into_writer(&engine, &mut buf)?;
175
176                let mut read = ReadBuf { buf: &buf, idx: 0 };
177                let engine_deser = ciborium::from_reader::<RsonpathEngine, _>(&mut read)?;
178
179                assert_eq!(&automaton, engine_deser.automaton());
180            }
181
182            #[test]
183            fn main_engine_json_roundtrips(ArbitraryJsonPathQuery { parsed, .. } in prop::arbitrary::any_with::<ArbitraryJsonPathQuery>(
184                ArbitraryJsonPathQueryParams {
185                    only_rsonpath_supported_subset: true,
186                    ..Default::default()
187                }
188            )) {
189                let automaton = match Automaton::new(&parsed) {
190                    Ok(x) => Ok(x),
191                    Err(crate::automaton::error::CompilerError::QueryTooComplex(_)) => Err(TestCaseError::Reject("query too complex".into())),
192                    Err(e) => Err(e.into()),
193                }?;
194                let engine = RsonpathEngine::from_compiled_query(automaton.clone());
195
196                let json_str = serde_json::to_string(&engine)?;
197                let engine_deser = serde_json::from_str::<RsonpathEngine>(&json_str)?;
198
199                assert_eq!(&automaton, engine_deser.automaton());
200            }
201
202            #[test]
203            fn main_engine_message_pack_roundtrips(ArbitraryJsonPathQuery { parsed, .. } in prop::arbitrary::any_with::<ArbitraryJsonPathQuery>(
204                ArbitraryJsonPathQueryParams {
205                    only_rsonpath_supported_subset: true,
206                    ..Default::default()
207                }
208            )) {
209                let automaton = match Automaton::new(&parsed) {
210                    Ok(x) => Ok(x),
211                    Err(crate::automaton::error::CompilerError::QueryTooComplex(_)) => Err(TestCaseError::Reject("query too complex".into())),
212                    Err(e) => Err(e.into()),
213                }?;
214                let engine = RsonpathEngine::from_compiled_query(automaton.clone());
215
216                let buf = rmp_serde::to_vec(&engine)?;
217                let engine_deser = rmp_serde::from_slice::<RsonpathEngine>(&buf)?;
218
219                assert_eq!(&automaton, engine_deser.automaton());
220            }
221        }
222    }
223}