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 Past,
15 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}