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 => {
122 self.path.push(JsonPointerItem::Number(self.num));
123 }
124 }
125 self.num = 0;
126 }
127 TokenType::Wildcard => {
128 self.path.push(JsonPointerItem::Wildcard);
129 }
130 TokenType::Unknown if self.start_pos > 0 => {
131 self.path.push(JsonPointerItem::Key("".into()));
132 }
133 _ => (),
134 }
135 }
136}
137
138impl<'de, P: Property> serde::Deserialize<'de> for JsonPointer<P> {
139 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
140 where
141 D: serde::Deserializer<'de>,
142 {
143 <&str>::deserialize(deserializer).map(|s| JsonPointer::parse(s))
144 }
145}
146
147impl<P: Property> serde::Serialize for JsonPointer<P> {
148 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
149 where
150 S: serde::Serializer,
151 {
152 serializer.serialize_str(&self.to_string())
153 }
154}
155
156#[cfg(test)]
157mod tests {
158
159 use super::{JsonPointer, JsonPointerItem};
160 use crate::{Key, Null, Property};
161 use std::borrow::Cow;
162
163 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
164 enum TestProp {
165 Ids,
166 Id(String),
167 }
168
169 impl Property for TestProp {
170 fn try_parse(key: Option<&Key<'_, Self>>, value: &str) -> Option<Self> {
171 if let Some(Key::Property(TestProp::Ids)) = key {
172 Some(TestProp::Id(value.to_string()))
173 } else if value == "ids" {
174 Some(TestProp::Ids)
175 } else {
176 None
177 }
178 }
179
180 fn to_cow(&self) -> Cow<'static, str> {
181 match self {
182 TestProp::Ids => Cow::Borrowed("ids"),
183 TestProp::Id(s) => Cow::Owned(s.clone()),
184 }
185 }
186 }
187
188 #[test]
189 fn json_pointer_parse() {
190 for (input, output) in vec![
191 ("hello", vec![JsonPointerItem::<Null>::Key("hello".into())]),
192 ("9a", vec![JsonPointerItem::Key("9a".into())]),
193 ("a9", vec![JsonPointerItem::Key("a9".into())]),
194 ("*a", vec![JsonPointerItem::Key("*a".into())]),
195 (
196 "/hello/world",
197 vec![
198 JsonPointerItem::Key("hello".into()),
199 JsonPointerItem::Key("world".into()),
200 ],
201 ),
202 ("*", vec![JsonPointerItem::Wildcard]),
203 (
204 "/hello/*",
205 vec![
206 JsonPointerItem::Key("hello".into()),
207 JsonPointerItem::Wildcard,
208 ],
209 ),
210 ("1234", vec![JsonPointerItem::Number(1234)]),
211 (
212 "/hello/1234",
213 vec![
214 JsonPointerItem::Key("hello".into()),
215 JsonPointerItem::Number(1234),
216 ],
217 ),
218 ("~0~1", vec![JsonPointerItem::Key("~/".into())]),
219 (
220 "/hello/~0~1",
221 vec![
222 JsonPointerItem::Key("hello".into()),
223 JsonPointerItem::Key("~/".into()),
224 ],
225 ),
226 (
227 "/hello/1~0~1/*~1~0",
228 vec![
229 JsonPointerItem::Key("hello".into()),
230 JsonPointerItem::Key("1~/".into()),
231 JsonPointerItem::Key("*/~".into()),
232 ],
233 ),
234 (
235 "/hello/world/*/99",
236 vec![
237 JsonPointerItem::Key("hello".into()),
238 JsonPointerItem::Key("world".into()),
239 JsonPointerItem::Wildcard,
240 JsonPointerItem::Number(99),
241 ],
242 ),
243 ("/", vec![JsonPointerItem::Key("".into())]),
244 (
245 "///",
246 vec![
247 JsonPointerItem::Key("".into()),
248 JsonPointerItem::Key("".into()),
249 JsonPointerItem::Key("".into()),
250 ],
251 ),
252 ("", vec![JsonPointerItem::Root]),
253 ] {
254 assert_eq!(JsonPointer::parse(input).0, output, "{input}");
255 }
256 }
257
258 #[test]
259 fn json_pointer_parse_promotes_digit() {
260 let pointer = JsonPointer::<TestProp>::parse("ids/2");
261 assert_eq!(
262 pointer.0,
263 vec![
264 JsonPointerItem::Key(Key::Property(TestProp::Ids)),
265 JsonPointerItem::Key(Key::Property(TestProp::Id("2".to_string()))),
266 ]
267 );
268
269 let pointer = JsonPointer::<TestProp>::parse("ids/abc");
270 assert_eq!(
271 pointer.0,
272 vec![
273 JsonPointerItem::Key(Key::Property(TestProp::Ids)),
274 JsonPointerItem::Key(Key::Property(TestProp::Id("abc".to_string()))),
275 ]
276 );
277
278 let pointer = JsonPointer::<TestProp>::parse("other/2");
279 assert_eq!(
280 pointer.0,
281 vec![
282 JsonPointerItem::Key(Key::Owned("other".to_string())),
283 JsonPointerItem::Number(2),
284 ]
285 );
286 }
287}