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