1#[derive(Debug, Clone, PartialEq, Eq)]
2pub enum JSONKey<'a> {
3 Slice(&'a str),
4 Index(usize),
5}
6
7#[derive(Debug, PartialEq, Eq)]
8pub enum RootJSONValue<'a> {
9 String(&'a str),
10 Number(&'a str),
11 Boolean(bool),
12 Null,
13}
14
15#[derive(Debug)]
16pub enum JSONParseErrorReason {
17 ExpectedColon,
18 ExpectedEndOfValue,
19 ExpectedBracket,
21 ExpectedTrueFalseNull,
22 ExpectedKey,
23 ExpectedValue,
24 ExpectedEndOfMultilineComment,
25 ExpectedQuote,
27}
28
29#[derive(Debug)]
30pub struct JSONParseError {
31 pub at: usize,
32 pub reason: JSONParseErrorReason,
33}
34
35impl std::error::Error for JSONParseError {}
36
37impl std::fmt::Display for JSONParseError {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
39 f.write_fmt(format_args!(
40 "JSONParseError: {:?} at {:?}",
41 self.reason, self.at
42 ))
43 }
44}
45
46pub fn parse<'a>(
52 on: &'a str,
53 mut cb: impl for<'b> FnMut(&'b [JSONKey<'a>], RootJSONValue<'a>),
54) -> Result<usize, JSONParseError> {
55 parse_with_exit_signal(
56 on,
57 |k, v| {
58 cb(k, v);
59 false
60 },
61 false,
62 true,
63 )
64}
65
66enum State {
67 InKey {
68 escaped: bool,
69 start: usize,
70 },
71 Colon,
72 InObject,
73 Comment {
74 start: usize,
75 multiline: bool,
76 last_was_asterisk: bool,
77 hash: bool,
78 },
79 ExpectingValue,
80 StringValue {
81 start: usize,
82 escaped: bool,
83 },
84 NumberValue {
85 start: usize,
86 },
87 TrueFalseNull {
88 start: usize,
89 },
90 EndOfValue,
91}
92
93fn end_of_value(
96 idx: usize,
97 chr: char,
98 state: &mut State,
99 key_chain: &mut Vec<JSONKey<'_>>,
100 allow_comments: bool,
101) -> Result<(), JSONParseError> {
102 if chr == ',' {
103 if let Some(JSONKey::Index(i)) = key_chain.last_mut() {
104 *i += 1;
105 *state = State::ExpectingValue;
106 } else {
107 key_chain.pop();
108 *state = State::InObject;
109 }
110 } else if let ('}', Some(JSONKey::Slice(..))) = (chr, key_chain.last()) {
111 key_chain.pop();
113 } else if let (']', Some(JSONKey::Index(..))) = (chr, key_chain.last()) {
114 key_chain.pop();
116 } else if let (true, c @ ('/' | '#')) = (allow_comments, chr) {
117 key_chain.pop();
118 *state = State::Comment {
119 last_was_asterisk: false,
120 start: idx,
121 multiline: false,
122 hash: c == '#',
123 };
124 } else if !chr.is_whitespace() {
125 return Err(JSONParseError {
126 at: idx,
127 reason: JSONParseErrorReason::ExpectedEndOfValue,
128 });
129 }
130 Ok(())
131}
132
133#[allow(clippy::too_many_lines)]
139pub fn parse_with_exit_signal<'a>(
140 on: &'a str,
141 mut cb: impl for<'b> FnMut(&'b [JSONKey<'a>], RootJSONValue<'a>) -> bool,
142 exit_on_first_value: bool,
143 allow_comments: bool,
144) -> Result<usize, JSONParseError> {
145 let chars = on.char_indices();
146
147 let mut key_chain = Vec::new();
148 let mut state = State::ExpectingValue;
149
150 for (idx, chr) in chars {
151 match state {
152 State::InKey {
153 start,
154 ref mut escaped,
155 } => {
156 if !*escaped && chr == '"' {
157 key_chain.push(JSONKey::Slice(&on[start..idx]));
158 state = State::Colon;
159 } else {
160 *escaped = chr == '\\';
161 }
162 }
163 State::StringValue {
164 start,
165 ref mut escaped,
166 } => {
167 if !*escaped && chr == '"' {
168 state = State::EndOfValue;
169 let res = cb(&key_chain, RootJSONValue::String(&on[start..idx]));
170 if res {
171 return Ok(idx + chr.len_utf8());
172 }
173 } else {
174 *escaped = chr == '\\';
175 }
176 }
177 State::Colon => {
178 if chr == ':' {
179 state = State::ExpectingValue;
180 } else if !chr.is_whitespace() {
181 return Err(JSONParseError {
182 at: idx,
183 reason: JSONParseErrorReason::ExpectedColon,
184 });
185 }
186 }
187 State::EndOfValue => {
188 end_of_value(idx, chr, &mut state, &mut key_chain, allow_comments)?;
189
190 if exit_on_first_value && key_chain.is_empty() && chr != ',' {
191 return Ok(idx + chr.len_utf8());
192 }
193 }
194 State::Comment {
195 ref mut last_was_asterisk,
196 ref mut multiline,
197 hash,
198 start,
199 } => {
200 if chr == '\n' && !*multiline {
201 if let Some(JSONKey::Index(..)) = key_chain.last() {
202 state = State::ExpectingValue;
203 } else {
204 state = State::InObject;
205 }
206 } else if chr == '*' && start + 1 == idx && !hash {
207 *multiline = true;
208 } else if *multiline {
209 if *last_was_asterisk && chr == '/' {
210 if let Some(JSONKey::Index(..)) = key_chain.last() {
211 state = State::ExpectingValue;
212 } else {
213 state = State::InObject;
214 }
215 } else {
216 *last_was_asterisk = chr == '*';
217 }
218 }
219 }
220 State::ExpectingValue => {
221 state = match chr {
222 '{' => State::InObject,
223 '[' => {
224 key_chain.push(JSONKey::Index(0));
225 State::ExpectingValue
226 }
227 '"' => State::StringValue {
228 start: idx + '"'.len_utf8(),
229 escaped: false,
230 },
231 c @ ('/' | '#') if allow_comments => State::Comment {
232 last_was_asterisk: false,
233 start: idx,
234 multiline: false,
235 hash: c == '#',
236 },
237 '0'..='9' | '-' => State::NumberValue { start: idx },
238 't' | 'f' | 'n' => State::TrueFalseNull { start: idx },
239 chr if chr.is_whitespace() => state,
240 _ => {
241 return Err(JSONParseError {
242 at: idx,
243 reason: JSONParseErrorReason::ExpectedValue,
244 })
245 }
246 }
247 }
248 State::InObject => {
249 if chr == '"' {
250 state = State::InKey {
251 escaped: false,
252 start: idx + '"'.len_utf8(),
253 };
254 } else if chr == '}' {
255 if let Some(JSONKey::Index(..)) = key_chain.last() {
256 state = State::ExpectingValue;
257 } else {
258 state = State::InObject;
259 }
260 } else if let (true, c @ ('/' | '#')) = (allow_comments, chr) {
261 state = State::Comment {
262 last_was_asterisk: false,
263 start: idx,
264 multiline: false,
265 hash: c == '#',
266 };
267 } else if !chr.is_whitespace() {
268 return Err(JSONParseError {
269 at: idx,
270 reason: JSONParseErrorReason::ExpectedKey,
271 });
272 }
273 }
274 State::NumberValue { start } => {
275 if chr.is_whitespace() || matches!(chr, '}' | ',' | ']') {
277 let res = cb(&key_chain, RootJSONValue::Number(&on[start..idx]));
278 if res {
279 return Ok(idx);
280 }
281 state = State::EndOfValue;
282 end_of_value(idx, chr, &mut state, &mut key_chain, allow_comments)?;
283 }
284 }
285 State::TrueFalseNull { start } => {
286 let diff = idx - start + 1;
287 if diff < 4 {
288 } else if diff == 4 {
290 match &on[start..=idx] {
291 "true" => {
292 let res = cb(&key_chain, RootJSONValue::Boolean(true));
293 if res {
294 return Ok(idx + chr.len_utf8());
295 }
296 state = State::EndOfValue;
297 }
298 "null" => {
299 let res = cb(&key_chain, RootJSONValue::Null);
300 if res {
301 return Ok(idx + chr.len_utf8());
302 }
303 state = State::EndOfValue;
304 }
305 "fals" => {}
306 _ => {
307 return Err(JSONParseError {
308 at: idx,
309 reason: JSONParseErrorReason::ExpectedTrueFalseNull,
310 })
311 }
312 }
313 } else if let "false" = &on[start..=idx] {
314 let res = cb(&key_chain, RootJSONValue::Boolean(false));
315 if res {
316 return Ok(idx + chr.len_utf8());
317 }
318 state = State::EndOfValue;
319 } else {
320 return Err(JSONParseError {
321 at: idx,
322 reason: JSONParseErrorReason::ExpectedTrueFalseNull,
323 });
324 }
325 }
326 }
327 }
328
329 match state {
330 State::InKey { .. } | State::StringValue { .. } => {
331 return Err(JSONParseError {
332 at: on.len(),
333 reason: JSONParseErrorReason::ExpectedQuote,
334 })
335 }
336 State::Colon => {
337 return Err(JSONParseError {
338 at: on.len(),
339 reason: JSONParseErrorReason::ExpectedColon,
340 });
341 }
342 State::Comment { multiline, .. } => {
343 if multiline {
344 return Err(JSONParseError {
345 at: on.len(),
346 reason: JSONParseErrorReason::ExpectedEndOfMultilineComment,
347 });
348 }
349 }
350 State::EndOfValue | State::ExpectingValue => {
351 if !key_chain.is_empty() {
352 return Err(JSONParseError {
353 at: on.len(),
354 reason: JSONParseErrorReason::ExpectedBracket,
355 });
356 }
357 }
358 State::InObject => {
359 return Err(JSONParseError {
360 at: on.len(),
361 reason: JSONParseErrorReason::ExpectedBracket,
362 });
363 }
364 State::NumberValue { start } => {
365 let _result = cb(&key_chain, RootJSONValue::Number(&on[start..]));
367 }
368 State::TrueFalseNull { start: _ } => {
369 return Err(JSONParseError {
370 at: on.len(),
371 reason: JSONParseErrorReason::ExpectedTrueFalseNull,
372 })
373 }
374 }
375
376 Ok(on.len())
377}