1use super::ParseOptions;
2use super::errors::*;
3use super::tokens::Token;
4use super::value::*;
5use crate::parser::JsoncParser;
6use crate::value::Map;
7
8pub fn parse_to_value<'a>(text: &'a str, options: &ParseOptions) -> Result<Option<JsonValue<'a>>, ParseError> {
20 let mut parser = JsoncParser::new(text, options);
21
22 let token = parser.scan()?;
23 let value = match token {
24 Some(token) => Some(parse_value(&mut parser, token)?),
25 None => return Ok(None),
26 };
27
28 if parser.scan()?.is_some() {
29 return Err(
30 parser
31 .scanner
32 .create_error_for_current_token(ParseErrorKind::MultipleRootJsonValues),
33 );
34 }
35
36 Ok(value)
37}
38
39fn parse_value<'a>(parser: &mut JsoncParser<'a>, token: Token<'a>) -> Result<JsonValue<'a>, ParseError> {
40 match token {
41 Token::OpenBrace => parse_object(parser),
42 Token::OpenBracket => parse_array(parser),
43 Token::String(s) => Ok(JsonValue::String(s)),
44 Token::Number(n) => Ok(JsonValue::Number(n)),
45 Token::Boolean(b) => Ok(JsonValue::Boolean(b)),
46 Token::Null => Ok(JsonValue::Null),
47 other => Err(parser.unexpected_token_error(&other)),
48 }
49}
50
51fn parse_object<'a>(parser: &mut JsoncParser<'a>) -> Result<JsonValue<'a>, ParseError> {
52 parser.enter_container()?;
53 let mut props = Map::new();
54 let mut first = true;
55
56 loop {
57 match parser.scan_object_entry(first)? {
58 None => break,
59 Some(key) => {
60 first = false;
61 let key_string = key.into_string();
62 parser.scan_object_colon()?;
63 match parser.scan()? {
64 Some(value_token) => {
65 let value = parse_value(parser, value_token)?;
66 props.insert(key_string, value);
67 }
68 None => {
69 parser.exit_container();
70 return Err(
71 parser
72 .scanner
73 .create_error_for_current_token(ParseErrorKind::ExpectedObjectValue),
74 );
75 }
76 }
77 }
78 }
79 }
80
81 parser.exit_container();
82 Ok(JsonValue::Object(JsonObject::new(props)))
83}
84
85fn parse_array<'a>(parser: &mut JsoncParser<'a>) -> Result<JsonValue<'a>, ParseError> {
86 parser.enter_container()?;
87 let mut elements = Vec::new();
88
89 let mut token = parser.scan()?;
90
91 loop {
92 match token {
93 Some(Token::CloseBracket) => break,
94 None => {
95 parser.exit_container();
96 return Err(
97 parser
98 .scanner
99 .create_error_for_current_token(ParseErrorKind::UnterminatedArray),
100 );
101 }
102 Some(value_token) => {
103 elements.push(parse_value(parser, value_token)?);
104 }
105 }
106 token = parser.scan_array_comma()?;
107 }
108
109 parser.exit_container();
110 Ok(JsonValue::Array(JsonArray::new(elements)))
111}
112
113#[cfg(test)]
114mod tests {
115 use crate::errors::ParseErrorKind;
116
117 use super::*;
118 use std::borrow::Cow;
119
120 #[test]
121 fn it_should_parse_object() {
122 let value = parse_to_value(
123 r#"{
124 "a": null,
125 "b": [null, "text"],
126 "c": true,
127 d: 25.55
128}"#,
129 &Default::default(),
130 )
131 .unwrap()
132 .unwrap();
133
134 let mut object_map = Map::new();
135 object_map.insert(String::from("a"), JsonValue::Null);
136 object_map.insert(
137 String::from("b"),
138 JsonValue::Array(vec![JsonValue::Null, JsonValue::String(Cow::Borrowed("text"))].into()),
139 );
140 object_map.insert(String::from("c"), JsonValue::Boolean(true));
141 object_map.insert(String::from("d"), JsonValue::Number("25.55"));
142 assert_eq!(value, JsonValue::Object(object_map.into()));
143 }
144
145 #[test]
146 fn it_should_parse_boolean_false() {
147 let value = parse_to_value("false", &Default::default()).unwrap().unwrap();
148 assert_eq!(value, JsonValue::Boolean(false));
149 let value = parse_to_value("true", &Default::default()).unwrap().unwrap();
150 assert_eq!(value, JsonValue::Boolean(true));
151 }
152
153 #[test]
154 fn it_should_parse_boolean_true() {
155 let value = parse_to_value("true", &Default::default()).unwrap().unwrap();
156 assert_eq!(value, JsonValue::Boolean(true));
157 }
158
159 #[test]
160 fn it_should_parse_number() {
161 let value = parse_to_value("50", &Default::default()).unwrap().unwrap();
162 assert_eq!(value, JsonValue::Number("50"));
163 }
164
165 #[test]
166 fn it_should_parse_string() {
167 let value = parse_to_value(r#""test""#, &Default::default()).unwrap().unwrap();
168 assert_eq!(value, JsonValue::String(Cow::Borrowed("test")));
169 }
170
171 #[test]
172 fn it_should_parse_string_with_quotes() {
173 let value = parse_to_value(r#""echo \"test\"""#, &Default::default())
174 .unwrap()
175 .unwrap();
176 assert_eq!(value, JsonValue::String(Cow::Borrowed(r#"echo "test""#)));
177 }
178
179 #[test]
180 fn it_should_parse_array() {
181 let value = parse_to_value(r#"[false, true]"#, &Default::default())
182 .unwrap()
183 .unwrap();
184 assert_eq!(
185 value,
186 JsonValue::Array(vec![JsonValue::Boolean(false), JsonValue::Boolean(true)].into())
187 );
188 }
189
190 #[test]
191 fn it_should_parse_null() {
192 let value = parse_to_value("null", &Default::default()).unwrap().unwrap();
193 assert_eq!(value, JsonValue::Null);
194 }
195
196 #[test]
197 fn it_should_parse_empty() {
198 let value = parse_to_value("", &Default::default()).unwrap();
199 assert!(value.is_none());
200 }
201
202 #[test]
203 fn error_unexpected_token() {
204 let err = parse_to_value("{\n \"a\":\u{200b}5 }", &Default::default())
205 .err()
206 .unwrap();
207 assert_eq!(err.range().start, 8);
208 assert_eq!(err.range().end, 11);
209 assert!(matches!(err.kind(), ParseErrorKind::UnexpectedToken));
210 }
211
212 #[test]
213 fn it_should_parse_surrogate_pair() {
214 let src = r#""\uD834\uDD1E""#;
216 let v = parse_to_value(src, &Default::default()).unwrap().unwrap();
217 if let JsonValue::String(s) = v {
218 assert_eq!("\u{1D11E}", s.as_ref());
219 } else {
220 panic!("Expected string value, got {:?}", v);
221 }
222 }
223
224 #[test]
225 fn it_should_parse_multiple_surrogate_pairs() {
226 let src = r#""\uD834\uDD1E\uD834\uDD1E""#;
227 let v = parse_to_value(src, &Default::default()).unwrap().unwrap();
228 if let JsonValue::String(s) = v {
229 assert_eq!("\u{1D11E}\u{1D11E}", s.as_ref());
230 } else {
231 panic!("Expected string value, got {:?}", v);
232 }
233 }
234
235 #[test]
236 fn it_should_parse_mixed_escapes_with_surrogate_pairs() {
237 let src = r#""\u0041\uD834\uDD1E\u0042""#;
239 let v = parse_to_value(src, &Default::default()).unwrap().unwrap();
240 if let JsonValue::String(s) = v {
241 assert_eq!("A\u{1D11E}B", s.as_ref());
242 } else {
243 panic!("Expected string value, got {:?}", v);
244 }
245 }
246
247 #[test]
248 fn it_should_error_on_unpaired_high_surrogate_with_text() {
249 let src = r#""\uD834x""#;
250 let err = parse_to_value(src, &Default::default()).err().unwrap();
251 assert!(err.to_string().contains("unpaired high surrogate"));
252 }
253
254 #[test]
255 fn it_should_error_on_unpaired_high_surrogate_at_eof() {
256 let src = r#""\uD834""#;
257 let err = parse_to_value(src, &Default::default()).err().unwrap();
258 assert!(err.to_string().contains("unpaired high surrogate"));
259 }
260
261 #[test]
262 fn it_should_error_on_high_surrogate_followed_by_non_low_surrogate() {
263 let src = r#""\uD834\u0041""#;
264 let err = parse_to_value(src, &Default::default()).err().unwrap();
265 assert!(err.to_string().contains("not followed by low surrogate"));
266 }
267
268 #[test]
269 fn it_should_error_on_unpaired_low_surrogate() {
270 let src = r#""\uDC00""#;
272 let err = parse_to_value(src, &Default::default()).err().unwrap();
273 assert!(err.to_string().contains("unpaired low surrogate"));
274 }
275
276 #[test]
277 fn it_should_error_when_arrays_are_deeply_nested() {
278 let mut json = String::new();
280 let depth = 30_000;
281
282 for _ in 0..depth {
283 json += "[";
284 }
285
286 for _ in 0..depth {
287 json += "]";
288 }
289
290 let result = parse_to_value(&json, &ParseOptions::default());
291
292 match result {
293 Ok(_) => panic!("Expected error, but did not find one."),
294 Err(err) => assert_eq!(err.to_string(), "Maximum nesting depth exceeded on line 1 column 513"),
295 }
296 }
297
298 #[test]
299 fn it_should_error_when_objects_are_deeply_nested() {
300 let mut json = String::new();
302 let depth = 30_000;
303
304 for _ in 0..depth {
305 json += "{\"q\":";
306 }
307
308 for _ in 0..depth {
309 json += "}";
310 }
311
312 let result = parse_to_value(&json, &ParseOptions::default());
313
314 match result {
315 Ok(_) => panic!("Expected error, but did not find one."),
316 Err(err) => assert_eq!(err.to_string(), "Maximum nesting depth exceeded on line 1 column 2561"),
317 }
318 }
319
320 #[track_caller]
323 fn assert_has_error(text: &str, message: &str) {
324 let result = parse_to_value(text, &Default::default());
325 match result {
326 Ok(_) => panic!("Expected error, but did not find one."),
327 Err(err) => assert_eq!(err.to_string(), message),
328 }
329 }
330
331 #[track_caller]
332 fn assert_has_strict_error(text: &str, message: &str) {
333 let result = parse_to_value(
334 text,
335 &ParseOptions {
336 allow_comments: false,
337 allow_loose_object_property_names: false,
338 allow_trailing_commas: false,
339 allow_missing_commas: false,
340 allow_single_quoted_strings: false,
341 allow_hexadecimal_numbers: false,
342 allow_unary_plus_numbers: false,
343 },
344 );
345 match result {
346 Ok(_) => panic!("Expected error, but did not find one."),
347 Err(err) => assert_eq!(err.to_string(), message),
348 }
349 }
350
351 #[test]
352 fn it_should_error_when_has_multiple_values() {
353 assert_has_error(
354 "[][]",
355 "Text cannot contain more than one JSON value on line 1 column 3",
356 );
357 }
358
359 #[test]
360 fn it_should_error_when_object_is_not_terminated() {
361 assert_has_error("{", "Unterminated object on line 1 column 2");
362 }
363
364 #[test]
365 fn it_should_error_when_object_has_unexpected_token() {
366 assert_has_error("{ [] }", "Unexpected token in object on line 1 column 3");
367 }
368
369 #[test]
370 fn it_should_error_when_object_has_two_non_string_tokens() {
371 assert_has_error(
372 "{ asdf asdf: 5 }",
373 "Expected colon after the string or word in object property on line 1 column 8",
374 );
375 }
376
377 #[test]
378 fn it_should_error_when_array_is_not_terminated() {
379 assert_has_error("[", "Unterminated array on line 1 column 2");
380 }
381
382 #[test]
383 fn it_should_error_when_array_has_unexpected_token() {
384 assert_has_error("[:]", "Unexpected colon on line 1 column 2");
385 }
386
387 #[test]
388 fn it_should_error_when_comment_block_not_closed() {
389 assert_has_error("/* test", "Unterminated comment block on line 1 column 1");
390 }
391
392 #[test]
393 fn it_should_error_when_string_lit_not_closed() {
394 assert_has_error("\" test", "Unterminated string literal on line 1 column 1");
395 }
396
397 #[test]
398 fn strict_should_error_object_trailing_comma() {
399 assert_has_strict_error(
400 r#"{ "test": 5, }"#,
401 "Trailing commas are not allowed on line 1 column 12",
402 );
403 }
404
405 #[test]
406 fn strict_should_error_array_trailing_comma() {
407 assert_has_strict_error(r#"[ "test", ]"#, "Trailing commas are not allowed on line 1 column 9");
408 }
409
410 #[test]
411 fn strict_should_error_comment_line() {
412 assert_has_strict_error(r#"[ "test" ] // 1"#, "Comments are not allowed on line 1 column 12");
413 }
414
415 #[test]
416 fn strict_should_error_comment_block() {
417 assert_has_strict_error(r#"[ "test" /* 1 */]"#, "Comments are not allowed on line 1 column 10");
418 }
419
420 #[test]
421 fn strict_should_error_word_property() {
422 assert_has_strict_error(
423 r#"{ word: 5 }"#,
424 "Expected string for object property on line 1 column 3",
425 );
426 }
427
428 #[test]
429 fn strict_should_error_single_quoted_string() {
430 assert_has_strict_error(
431 r#"{ "key": 'value' }"#,
432 "Single-quoted strings are not allowed on line 1 column 10",
433 );
434 }
435
436 #[test]
437 fn strict_should_error_hexadecimal_number() {
438 assert_has_strict_error(
439 r#"{ "key": 0xFF }"#,
440 "Hexadecimal numbers are not allowed on line 1 column 10",
441 );
442 }
443
444 #[test]
445 fn strict_should_error_unary_plus_number() {
446 assert_has_strict_error(
447 r#"{ "key": +42 }"#,
448 "Unary plus on numbers is not allowed on line 1 column 10",
449 );
450 }
451
452 #[test]
453 fn missing_comma_between_properties() {
454 let text = r#"{
455 "name": "alice"
456 "age": 25
457}"#;
458 let value = parse_to_value(text, &Default::default()).unwrap().unwrap();
459 let obj = match &value {
460 JsonValue::Object(o) => o,
461 _ => panic!("Expected object"),
462 };
463 assert_eq!(obj.get_number("age").unwrap(), "25");
464
465 assert_has_strict_error(text, "Expected comma on line 2 column 18");
467 }
468
469 #[test]
470 fn missing_comma_with_comment_between_properties() {
471 let result = parse_to_value(
474 r#"{
475 "name": "alice" // comment here
476 "age": 25
477}"#,
478 &ParseOptions {
479 allow_comments: true,
480 allow_missing_commas: false,
481 ..Default::default()
482 },
483 );
484 match result {
485 Ok(_) => panic!("Expected error, but did not find one."),
486 Err(err) => assert_eq!(err.to_string(), "Expected comma on line 2 column 18"),
487 }
488 }
489
490 #[test]
491 fn it_should_parse_unquoted_keys_with_hex_and_trailing_comma() {
492 let text = r#"{
493 CP_CanFuncReqId: 0x7DF, // 2015
494 }"#;
495 let value = parse_to_value(text, &Default::default()).unwrap().unwrap();
496 let obj = match &value {
497 JsonValue::Object(o) => o,
498 _ => panic!("Expected object"),
499 };
500 assert_eq!(obj.get_number("CP_CanFuncReqId").unwrap(), "0x7DF");
501 }
502
503 #[test]
504 fn it_should_parse_unary_plus_numbers() {
505 let value = parse_to_value(r#"{ "test": +42 }"#, &Default::default())
506 .unwrap()
507 .unwrap();
508 let obj = match &value {
509 JsonValue::Object(o) => o,
510 _ => panic!("Expected object"),
511 };
512 assert_eq!(obj.get_number("test").unwrap(), "+42");
513 }
514
515 #[test]
516 fn it_should_parse_large_shallow_objects() {
517 let mut json = "{\"q\":[".to_string();
519 let size = 1_000;
520
521 for _ in 0..size {
522 json += "{\"q\":[{}]}, [\"hello\"], ";
523 }
524
525 json += "]}";
526
527 let result = parse_to_value(&json, &ParseOptions::default());
528 assert!(result.is_ok());
529 }
530}