1use crate::{JsonPointer, JsonPointerItem, Key, Property};
8use std::borrow::Cow;
9
10enum TokenType {
11 Unknown,
12 Number,
13 String,
14 Wildcard,
15 Escaped,
16}
17
18struct State<P: Property> {
19 num: u64,
20 buf: Vec<u8>,
21 token: TokenType,
22 start_pos: usize,
23 path: Vec<JsonPointerItem<P>>,
24}
25
26impl<P: Property> JsonPointer<P> {
27 pub fn parse(value: &str) -> Self {
28 let mut state = State {
29 num: 0,
30 buf: Vec::new(),
31 token: TokenType::Unknown,
32 start_pos: 0,
33 path: Vec::new(),
34 };
35 let value = value.as_bytes();
36 let mut iter = value.iter().enumerate();
37
38 while let Some((pos, &ch)) = iter.next() {
39 match (ch, &state.token) {
40 (b'0'..=b'9', TokenType::Unknown | TokenType::Number) => {
41 state.num = state
42 .num
43 .saturating_mul(10)
44 .saturating_add((ch - b'0') as u64);
45 state.token = TokenType::Number;
46 }
47 (b'*', TokenType::Unknown) => {
48 state.token = TokenType::Wildcard;
49 }
50 (b'0', TokenType::Escaped) => {
51 state.buf.push(b'~');
52 state.token = TokenType::String;
53 }
54 (b'1', TokenType::Escaped) => {
55 state.buf.push(b'/');
56 state.token = TokenType::String;
57 }
58 (b'/', _) => {
59 state.process(&value[state.start_pos..pos]);
60 state.token = TokenType::Unknown;
61 state.start_pos = pos + 1;
62 }
63 (_, _) => {
64 if matches!(&state.token, TokenType::Number | TokenType::Wildcard)
65 && pos > state.start_pos
66 {
67 state
68 .buf
69 .extend_from_slice(value.get(state.start_pos..pos).unwrap_or_default());
70 }
71
72 state.token = match ch {
73 b'~' if !matches!(&state.token, TokenType::Escaped) => TokenType::Escaped,
74 b'\\' => {
75 state
76 .buf
77 .push(iter.next().map(|(_, &ch)| ch).unwrap_or(b'\\'));
78 TokenType::String
79 }
80 _ => {
81 state.buf.push(ch);
82 TokenType::String
83 }
84 };
85 }
86 }
87 }
88
89 state.process(value.get(state.start_pos..).unwrap_or_default());
90
91 if state.path.is_empty() {
92 state.path.push(JsonPointerItem::Root);
93 }
94
95 JsonPointer(state.path)
96 }
97}
98
99impl<P: Property> State<P> {
100 pub fn process(&mut self, token_bytes: &[u8]) {
101 match self.token {
102 TokenType::String => {
103 let item = std::str::from_utf8(&self.buf).unwrap_or_default();
104 match P::try_parse(self.path.last().and_then(|item| item.as_key()), item) {
105 Some(prop) => {
106 self.path.push(JsonPointerItem::Key(Key::Property(prop)));
107 }
108 None => {
109 self.path
110 .push(JsonPointerItem::Key(Key::Owned(item.to_string())));
111 }
112 }
113
114 self.buf.clear();
115 }
116 TokenType::Number => {
117 let item = std::str::from_utf8(token_bytes).unwrap_or_default();
118 match P::try_parse(self.path.last().and_then(|item| item.as_key()), item) {
119 Some(prop) => {
120 self.path.push(JsonPointerItem::Key(Key::Property(prop)));
121 }
122 None if token_bytes.first() == Some(&b'0') && token_bytes.len() > 1 => {
123 self.path
124 .push(JsonPointerItem::Key(Key::Owned(item.to_string())));
125 }
126 None => {
127 self.path.push(JsonPointerItem::Number(self.num));
128 }
129 }
130 self.num = 0;
131 }
132 TokenType::Wildcard => {
133 self.path.push(JsonPointerItem::Wildcard);
134 }
135 TokenType::Unknown if self.start_pos > 0 => {
136 self.path.push(JsonPointerItem::Key("".into()));
137 }
138 _ => (),
139 }
140 }
141}
142
143impl<'de, P: Property> serde::Deserialize<'de> for JsonPointer<P> {
144 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
145 where
146 D: serde::Deserializer<'de>,
147 {
148 <Cow<'de, str>>::deserialize(deserializer).map(|s| JsonPointer::parse(s.as_ref()))
149 }
150}
151
152impl<P: Property> serde::Serialize for JsonPointer<P> {
153 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
154 where
155 S: serde::Serializer,
156 {
157 serializer.serialize_str(&self.to_string())
158 }
159}
160
161#[cfg(test)]
162mod tests {
163
164 use super::{JsonPointer, JsonPointerItem};
165 use crate::{Key, Null, Property};
166 use std::borrow::Cow;
167
168 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
169 enum TestProp {
170 Ids,
171 Id(String),
172 }
173
174 impl Property for TestProp {
175 fn try_parse(key: Option<&Key<'_, Self>>, value: &str) -> Option<Self> {
176 if let Some(Key::Property(TestProp::Ids)) = key {
177 Some(TestProp::Id(value.to_string()))
178 } else if value == "ids" {
179 Some(TestProp::Ids)
180 } else {
181 None
182 }
183 }
184
185 fn to_cow(&self) -> Cow<'static, str> {
186 match self {
187 TestProp::Ids => Cow::Borrowed("ids"),
188 TestProp::Id(s) => Cow::Owned(s.clone()),
189 }
190 }
191 }
192
193 #[test]
194 fn json_pointer_parse() {
195 for (input, output) in vec![
196 ("hello", vec![JsonPointerItem::<Null>::Key("hello".into())]),
197 ("9a", vec![JsonPointerItem::Key("9a".into())]),
198 ("a9", vec![JsonPointerItem::Key("a9".into())]),
199 ("*a", vec![JsonPointerItem::Key("*a".into())]),
200 (
201 "/hello/world",
202 vec![
203 JsonPointerItem::Key("hello".into()),
204 JsonPointerItem::Key("world".into()),
205 ],
206 ),
207 ("*", vec![JsonPointerItem::Wildcard]),
208 (
209 "/hello/*",
210 vec![
211 JsonPointerItem::Key("hello".into()),
212 JsonPointerItem::Wildcard,
213 ],
214 ),
215 ("1234", vec![JsonPointerItem::Number(1234)]),
216 (
217 "/hello/1234",
218 vec![
219 JsonPointerItem::Key("hello".into()),
220 JsonPointerItem::Number(1234),
221 ],
222 ),
223 (
224 "/hello/01",
225 vec![
226 JsonPointerItem::Key("hello".into()),
227 JsonPointerItem::Key("01".into()),
228 ],
229 ),
230 ("~0~1", vec![JsonPointerItem::Key("~/".into())]),
231 (
232 "/hello/~0~1",
233 vec![
234 JsonPointerItem::Key("hello".into()),
235 JsonPointerItem::Key("~/".into()),
236 ],
237 ),
238 (
239 "/hello/1~0~1/*~1~0",
240 vec![
241 JsonPointerItem::Key("hello".into()),
242 JsonPointerItem::Key("1~/".into()),
243 JsonPointerItem::Key("*/~".into()),
244 ],
245 ),
246 (
247 "/hello/world/*/99",
248 vec![
249 JsonPointerItem::Key("hello".into()),
250 JsonPointerItem::Key("world".into()),
251 JsonPointerItem::Wildcard,
252 JsonPointerItem::Number(99),
253 ],
254 ),
255 ("/", vec![JsonPointerItem::Key("".into())]),
256 (
257 "///",
258 vec![
259 JsonPointerItem::Key("".into()),
260 JsonPointerItem::Key("".into()),
261 JsonPointerItem::Key("".into()),
262 ],
263 ),
264 ("", vec![JsonPointerItem::Root]),
265 ] {
266 assert_eq!(JsonPointer::parse(input).0, output, "{input}");
267 }
268 }
269
270 #[test]
271 fn json_pointer_parse_promotes_digit() {
272 let pointer = JsonPointer::<TestProp>::parse("ids/2");
273 assert_eq!(
274 pointer.0,
275 vec![
276 JsonPointerItem::Key(Key::Property(TestProp::Ids)),
277 JsonPointerItem::Key(Key::Property(TestProp::Id("2".to_string()))),
278 ]
279 );
280
281 let pointer = JsonPointer::<TestProp>::parse("ids/abc");
282 assert_eq!(
283 pointer.0,
284 vec![
285 JsonPointerItem::Key(Key::Property(TestProp::Ids)),
286 JsonPointerItem::Key(Key::Property(TestProp::Id("abc".to_string()))),
287 ]
288 );
289
290 let pointer = JsonPointer::<TestProp>::parse("other/2");
291 assert_eq!(
292 pointer.0,
293 vec![
294 JsonPointerItem::Key(Key::Owned("other".to_string())),
295 JsonPointerItem::Number(2),
296 ]
297 );
298 }
299
300 #[test]
301 fn json_pointer_parse_leading_zero_is_string() {
302 let pointer = JsonPointer::<TestProp>::parse("other/07");
303 assert_eq!(
304 pointer.0,
305 vec![
306 JsonPointerItem::Key(Key::Owned("other".to_string())),
307 JsonPointerItem::Key(Key::Owned("07".to_string())),
308 ]
309 );
310
311 let pointer = JsonPointer::<TestProp>::parse("other/00");
312 assert_eq!(
313 pointer.0,
314 vec![
315 JsonPointerItem::Key(Key::Owned("other".to_string())),
316 JsonPointerItem::Key(Key::Owned("00".to_string())),
317 ]
318 );
319
320 let pointer = JsonPointer::<TestProp>::parse("other/0");
321 assert_eq!(
322 pointer.0,
323 vec![
324 JsonPointerItem::Key(Key::Owned("other".to_string())),
325 JsonPointerItem::Number(0),
326 ]
327 );
328
329 let pointer = JsonPointer::<TestProp>::parse("other/70");
330 assert_eq!(
331 pointer.0,
332 vec![
333 JsonPointerItem::Key(Key::Owned("other".to_string())),
334 JsonPointerItem::Number(70),
335 ]
336 );
337 }
338
339 #[test]
340 fn deserializes_pointers_with_escaped_solidus() {
341 let plain: JsonPointer<Null> = serde_json::from_str(r#""/list/*/id""#).expect("plain");
342 let escaped: JsonPointer<Null> =
343 serde_json::from_str(r#""\/list\/*\/id""#).expect("escaped solidus must deserialize");
344
345 assert_eq!(plain, escaped);
346 }
347}