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 mut iter = value.as_bytes().iter().enumerate();
35
36 while let Some((pos, &ch)) = iter.next() {
37 match (ch, &state.token) {
38 (b'0'..=b'9', TokenType::Unknown | TokenType::Number) => {
39 state.num = state
40 .num
41 .saturating_mul(10)
42 .saturating_add((ch - b'0') as u64);
43 state.token = TokenType::Number;
44 }
45 (b'*', TokenType::Unknown) => {
46 state.token = TokenType::Wildcard;
47 }
48 (b'0', TokenType::Escaped) => {
49 state.buf.push(b'~');
50 state.token = TokenType::String;
51 }
52 (b'1', TokenType::Escaped) => {
53 state.buf.push(b'/');
54 state.token = TokenType::String;
55 }
56 (b'/', _) => {
57 state.process();
58 state.token = TokenType::Unknown;
59 state.start_pos = pos + 1;
60 }
61 (_, _) => {
62 if matches!(&state.token, TokenType::Number | TokenType::Wildcard)
63 && pos > state.start_pos
64 {
65 state.buf.extend_from_slice(
66 value
67 .as_bytes()
68 .get(state.start_pos..pos)
69 .unwrap_or_default(),
70 );
71 }
72
73 state.token = match ch {
74 b'~' if !matches!(&state.token, TokenType::Escaped) => TokenType::Escaped,
75 b'\\' => {
76 state
77 .buf
78 .push(iter.next().map(|(_, &ch)| ch).unwrap_or(b'\\'));
79 TokenType::String
80 }
81 _ => {
82 state.buf.push(ch);
83 TokenType::String
84 }
85 };
86 }
87 }
88 }
89
90 state.process();
91
92 if state.path.is_empty() {
93 state.path.push(JsonPointerItem::Root);
94 }
95
96 JsonPointer(state.path)
97 }
98}
99
100impl<P: Property> State<P> {
101 pub fn process(&mut self) {
102 match self.token {
103 TokenType::String => {
104 let item = std::str::from_utf8(&self.buf).unwrap_or_default();
105 match P::try_parse(self.path.last().and_then(|item| item.as_key()), item) {
106 Some(prop) => {
107 self.path.push(JsonPointerItem::Key(Key::Property(prop)));
108 }
109 None => {
110 self.path
111 .push(JsonPointerItem::Key(Key::Owned(item.to_string())));
112 }
113 }
114
115 self.buf.clear();
116 }
117 TokenType::Number => {
118 self.path.push(JsonPointerItem::Number(self.num));
119 self.num = 0;
120 }
121 TokenType::Wildcard => {
122 self.path.push(JsonPointerItem::Wildcard);
123 }
124 TokenType::Unknown if self.start_pos > 0 => {
125 self.path.push(JsonPointerItem::Key("".into()));
126 }
127 _ => (),
128 }
129 }
130}
131
132#[cfg(test)]
133mod tests {
134
135 use crate::Null;
136
137 use super::{JsonPointer, JsonPointerItem};
138
139 #[test]
140 fn json_pointer_parse() {
141 for (input, output) in vec![
142 ("hello", vec![JsonPointerItem::<Null>::Key("hello".into())]),
143 ("9a", vec![JsonPointerItem::Key("9a".into())]),
144 ("a9", vec![JsonPointerItem::Key("a9".into())]),
145 ("*a", vec![JsonPointerItem::Key("*a".into())]),
146 (
147 "/hello/world",
148 vec![
149 JsonPointerItem::Key("hello".into()),
150 JsonPointerItem::Key("world".into()),
151 ],
152 ),
153 ("*", vec![JsonPointerItem::Wildcard]),
154 (
155 "/hello/*",
156 vec![
157 JsonPointerItem::Key("hello".into()),
158 JsonPointerItem::Wildcard,
159 ],
160 ),
161 ("1234", vec![JsonPointerItem::Number(1234)]),
162 (
163 "/hello/1234",
164 vec![
165 JsonPointerItem::Key("hello".into()),
166 JsonPointerItem::Number(1234),
167 ],
168 ),
169 ("~0~1", vec![JsonPointerItem::Key("~/".into())]),
170 (
171 "/hello/~0~1",
172 vec![
173 JsonPointerItem::Key("hello".into()),
174 JsonPointerItem::Key("~/".into()),
175 ],
176 ),
177 (
178 "/hello/1~0~1/*~1~0",
179 vec![
180 JsonPointerItem::Key("hello".into()),
181 JsonPointerItem::Key("1~/".into()),
182 JsonPointerItem::Key("*/~".into()),
183 ],
184 ),
185 (
186 "/hello/world/*/99",
187 vec![
188 JsonPointerItem::Key("hello".into()),
189 JsonPointerItem::Key("world".into()),
190 JsonPointerItem::Wildcard,
191 JsonPointerItem::Number(99),
192 ],
193 ),
194 ("/", vec![JsonPointerItem::Key("".into())]),
195 (
196 "///",
197 vec![
198 JsonPointerItem::Key("".into()),
199 JsonPointerItem::Key("".into()),
200 JsonPointerItem::Key("".into()),
201 ],
202 ),
203 ("", vec![JsonPointerItem::Root]),
204 ] {
205 assert_eq!(JsonPointer::parse(input).0, output, "{input}");
206 }
207 }
208}