1use super::ParseOptions;
2use super::errors::*;
3use super::tokens::Token;
4use super::value::*;
5use crate::map::Map;
6use crate::parser::JsoncParser;
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) => 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(Some(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::default();
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_cow();
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::default();
135 object_map.insert(Cow::Borrowed("a"), JsonValue::Null);
136 object_map.insert(
137 Cow::Borrowed("b"),
138 JsonValue::Array(vec![JsonValue::Null, JsonValue::String(Cow::Borrowed("text"))].into()),
139 );
140 object_map.insert(Cow::Borrowed("c"), JsonValue::Boolean(true));
141 object_map.insert(Cow::Borrowed("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 error_unexpected_word_should_have_full_range() {
214 let err = parse_to_value(r#"{ "test": asdf }"#, &Default::default())
217 .err()
218 .unwrap();
219 assert!(matches!(err.kind(), ParseErrorKind::UnexpectedWord));
220 assert_eq!(err.range().start, 10);
222 assert_eq!(err.range().end, 14);
223 }
224
225 #[test]
226 fn it_should_parse_surrogate_pair() {
227 let src = r#""\uD834\uDD1E""#;
229 let v = parse_to_value(src, &Default::default()).unwrap().unwrap();
230 if let JsonValue::String(s) = v {
231 assert_eq!("\u{1D11E}", s.as_ref());
232 } else {
233 panic!("Expected string value, got {:?}", v);
234 }
235 }
236
237 #[test]
238 fn it_should_parse_multiple_surrogate_pairs() {
239 let src = r#""\uD834\uDD1E\uD834\uDD1E""#;
240 let v = parse_to_value(src, &Default::default()).unwrap().unwrap();
241 if let JsonValue::String(s) = v {
242 assert_eq!("\u{1D11E}\u{1D11E}", s.as_ref());
243 } else {
244 panic!("Expected string value, got {:?}", v);
245 }
246 }
247
248 #[test]
249 fn it_should_parse_mixed_escapes_with_surrogate_pairs() {
250 let src = r#""\u0041\uD834\uDD1E\u0042""#;
252 let v = parse_to_value(src, &Default::default()).unwrap().unwrap();
253 if let JsonValue::String(s) = v {
254 assert_eq!("A\u{1D11E}B", s.as_ref());
255 } else {
256 panic!("Expected string value, got {:?}", v);
257 }
258 }
259
260 #[test]
261 fn it_should_error_on_unpaired_high_surrogate_with_text() {
262 let src = r#""\uD834x""#;
263 let err = parse_to_value(src, &Default::default()).err().unwrap();
264 assert!(err.to_string().contains("unpaired high surrogate"));
265 }
266
267 #[test]
268 fn it_should_error_on_unpaired_high_surrogate_at_eof() {
269 let src = r#""\uD834""#;
270 let err = parse_to_value(src, &Default::default()).err().unwrap();
271 assert!(err.to_string().contains("unpaired high surrogate"));
272 }
273
274 #[test]
275 fn it_should_error_on_high_surrogate_followed_by_non_low_surrogate() {
276 let src = r#""\uD834\u0041""#;
277 let err = parse_to_value(src, &Default::default()).err().unwrap();
278 assert!(err.to_string().contains("not followed by low surrogate"));
279 }
280
281 #[test]
282 fn it_should_error_on_unpaired_low_surrogate() {
283 let src = r#""\uDC00""#;
285 let err = parse_to_value(src, &Default::default()).err().unwrap();
286 assert!(err.to_string().contains("unpaired low surrogate"));
287 }
288
289 #[test]
290 fn it_should_error_when_arrays_are_deeply_nested() {
291 let mut json = String::new();
293 let depth = 30_000;
294
295 for _ in 0..depth {
296 json += "[";
297 }
298
299 for _ in 0..depth {
300 json += "]";
301 }
302
303 let result = parse_to_value(&json, &ParseOptions::default());
304
305 match result {
306 Ok(_) => panic!("Expected error, but did not find one."),
307 Err(err) => assert_eq!(err.to_string(), "Maximum nesting depth exceeded on line 1 column 513"),
308 }
309 }
310
311 #[test]
312 fn it_should_error_when_objects_are_deeply_nested() {
313 let mut json = String::new();
315 let depth = 30_000;
316
317 for _ in 0..depth {
318 json += "{\"q\":";
319 }
320
321 for _ in 0..depth {
322 json += "}";
323 }
324
325 let result = parse_to_value(&json, &ParseOptions::default());
326
327 match result {
328 Ok(_) => panic!("Expected error, but did not find one."),
329 Err(err) => assert_eq!(err.to_string(), "Maximum nesting depth exceeded on line 1 column 2561"),
330 }
331 }
332
333 #[track_caller]
336 fn assert_has_error(text: &str, message: &str) {
337 let result = parse_to_value(text, &Default::default());
338 match result {
339 Ok(_) => panic!("Expected error, but did not find one."),
340 Err(err) => assert_eq!(err.to_string(), message),
341 }
342 }
343
344 #[track_caller]
345 fn assert_has_strict_error(text: &str, message: &str) {
346 let result = parse_to_value(text, &strict_options());
347 match result {
348 Ok(_) => panic!("Expected error, but did not find one."),
349 Err(err) => assert_eq!(err.to_string(), message),
350 }
351 }
352
353 fn strict_options() -> ParseOptions {
354 ParseOptions {
355 allow_comments: false,
356 allow_loose_object_property_names: false,
357 allow_trailing_commas: false,
358 allow_missing_commas: false,
359 allow_single_quoted_strings: false,
360 allow_hexadecimal_numbers: false,
361 allow_unary_plus_numbers: false,
362 allow_bare_decimal_point_numbers: false,
363 allow_non_finite_numbers: false,
364 allow_extended_string_escapes: false,
365 }
366 }
367
368 #[test]
369 fn it_should_error_when_has_multiple_values() {
370 assert_has_error(
371 "[][]",
372 "Text cannot contain more than one JSON value on line 1 column 3",
373 );
374 }
375
376 #[test]
377 fn it_should_error_when_object_is_not_terminated() {
378 assert_has_error("{", "Unterminated object on line 1 column 2");
379 }
380
381 #[test]
382 fn it_should_error_when_object_has_unexpected_token() {
383 assert_has_error("{ [] }", "Unexpected token in object on line 1 column 3");
384 }
385
386 #[test]
387 fn it_should_error_when_object_has_two_non_string_tokens() {
388 assert_has_error(
389 "{ asdf asdf: 5 }",
390 "Expected colon after the string or word in object property on line 1 column 8",
391 );
392 }
393
394 #[test]
395 fn it_should_error_when_array_is_not_terminated() {
396 assert_has_error("[", "Unterminated array on line 1 column 2");
397 }
398
399 #[test]
400 fn it_should_error_when_array_has_unexpected_token() {
401 assert_has_error("[:]", "Unexpected colon on line 1 column 2");
402 }
403
404 #[test]
405 fn it_should_error_when_comment_block_not_closed() {
406 assert_has_error("/* test", "Unterminated comment block on line 1 column 1");
407 }
408
409 #[test]
410 fn it_should_error_when_string_lit_not_closed() {
411 assert_has_error("\" test", "Unterminated string literal on line 1 column 1");
412 }
413
414 #[test]
415 fn strict_should_error_object_trailing_comma() {
416 assert_has_strict_error(
417 r#"{ "test": 5, }"#,
418 "Trailing commas are not allowed on line 1 column 12",
419 );
420 }
421
422 #[test]
423 fn strict_should_error_array_trailing_comma() {
424 assert_has_strict_error(r#"[ "test", ]"#, "Trailing commas are not allowed on line 1 column 9");
425 }
426
427 #[test]
428 fn strict_should_error_comment_line() {
429 assert_has_strict_error(r#"[ "test" ] // 1"#, "Comments are not allowed on line 1 column 12");
430 }
431
432 #[test]
433 fn strict_should_error_comment_block() {
434 assert_has_strict_error(r#"[ "test" /* 1 */]"#, "Comments are not allowed on line 1 column 10");
435 }
436
437 #[test]
438 fn strict_should_error_word_property() {
439 assert_has_strict_error(
440 r#"{ word: 5 }"#,
441 "Expected string for object property on line 1 column 3",
442 );
443 }
444
445 #[test]
446 fn strict_should_error_single_quoted_string() {
447 assert_has_strict_error(
448 r#"{ "key": 'value' }"#,
449 "Single-quoted strings are not allowed on line 1 column 10",
450 );
451 }
452
453 #[test]
454 fn strict_should_error_hexadecimal_number() {
455 assert_has_strict_error(
456 r#"{ "key": 0xFF }"#,
457 "Hexadecimal numbers are not allowed on line 1 column 10",
458 );
459 }
460
461 #[test]
462 fn strict_should_error_unary_plus_number() {
463 assert_has_strict_error(
464 r#"{ "key": +42 }"#,
465 "Unary plus on numbers is not allowed on line 1 column 10",
466 );
467 }
468
469 #[test]
470 fn missing_comma_between_properties() {
471 let text = r#"{
472 "name": "alice"
473 "age": 25
474}"#;
475 let value = parse_to_value(text, &Default::default()).unwrap().unwrap();
476 let obj = match &value {
477 JsonValue::Object(o) => o,
478 _ => panic!("Expected object"),
479 };
480 assert_eq!(obj.get_number("age").unwrap(), "25");
481
482 assert_has_strict_error(text, "Expected comma on line 2 column 18");
484 }
485
486 #[test]
487 fn missing_comma_with_comment_between_properties() {
488 let result = parse_to_value(
491 r#"{
492 "name": "alice" // comment here
493 "age": 25
494}"#,
495 &ParseOptions {
496 allow_comments: true,
497 allow_missing_commas: false,
498 ..Default::default()
499 },
500 );
501 match result {
502 Ok(_) => panic!("Expected error, but did not find one."),
503 Err(err) => assert_eq!(err.to_string(), "Expected comma on line 2 column 18"),
504 }
505 }
506
507 #[test]
508 fn missing_comma_between_array_elements() {
509 let text = "[1 2]";
510 let value = parse_to_value(text, &Default::default()).unwrap().unwrap();
511 assert_eq!(
512 value,
513 JsonValue::Array(vec![JsonValue::Number("1"), JsonValue::Number("2")].into())
514 );
515
516 assert_has_strict_error(text, "Expected comma on line 1 column 3");
518 assert_has_strict_error("[01]", "Expected comma on line 1 column 3");
519 assert_has_strict_error(r#"["a" "b"]"#, "Expected comma on line 1 column 5");
520 assert_has_strict_error("[true false]", "Expected comma on line 1 column 6");
521 assert_has_strict_error("[null null]", "Expected comma on line 1 column 6");
522 assert_has_strict_error("[[1] [2]]", "Expected comma on line 1 column 5");
523 assert_has_strict_error(r#"[{"a":1} {"b":2}]"#, "Expected comma on line 1 column 9");
524 assert_has_strict_error("[1 2", "Expected comma on line 1 column 3");
525
526 assert_has_strict_error("[1", "Unterminated array on line 1 column 3");
528 assert_has_strict_error("[1 a ]", "Unexpected word on line 1 column 4");
529
530 for text in ["[]", "[ ]", "[1,2]", "[1 , 2]", "[[1],[2]]", r#"[{"a":1},{"b":2}]"#] {
531 parse_to_value(text, &strict_options()).unwrap();
532 }
533 }
534
535 #[test]
536 fn missing_comma_not_allowed_with_trailing_commas_allowed_in_array() {
537 let options = ParseOptions {
538 allow_missing_commas: false,
539 allow_trailing_commas: true,
540 ..Default::default()
541 };
542 for text in ["[]", "[1,]", "[1, 2,]", "[[1,],[2,],]"] {
543 parse_to_value(text, &options).unwrap();
544 }
545 }
546
547 #[test]
548 fn missing_comma_with_comment_between_array_elements() {
549 let result = parse_to_value(
552 r#"[
553 1 // comment here
554 2
555]"#,
556 &ParseOptions {
557 allow_comments: true,
558 allow_missing_commas: false,
559 ..Default::default()
560 },
561 );
562 match result {
563 Ok(_) => panic!("Expected error, but did not find one."),
564 Err(err) => assert_eq!(err.to_string(), "Expected comma on line 2 column 4"),
565 }
566 }
567
568 #[test]
569 fn it_should_parse_unquoted_keys_with_hex_and_trailing_comma() {
570 let text = r#"{
571 CP_CanFuncReqId: 0x7DF, // 2015
572 }"#;
573 let value = parse_to_value(text, &Default::default()).unwrap().unwrap();
574 let obj = match &value {
575 JsonValue::Object(o) => o,
576 _ => panic!("Expected object"),
577 };
578 assert_eq!(obj.get_number("CP_CanFuncReqId").unwrap(), "0x7DF");
579 }
580
581 #[test]
582 fn it_should_parse_keywords_as_loose_property_names() {
583 let value = parse_to_value(r#"{ true: 1, false: 2 null: 3 }"#, &Default::default())
584 .unwrap()
585 .unwrap();
586 let obj = match &value {
587 JsonValue::Object(o) => o,
588 _ => panic!("Expected object"),
589 };
590 assert_eq!(obj.get_number("true").unwrap(), "1");
591 assert_eq!(obj.get_number("false").unwrap(), "2");
592 assert_eq!(obj.get_number("null").unwrap(), "3");
593 }
594
595 #[test]
596 fn it_should_parse_loose_property_names_with_dollar_signs() {
597 let value = parse_to_value(
598 r#"{ $: 1, _$_: 2, $_$hello123: 3, NaN$: 4, true$: 5 }"#,
599 &Default::default(),
600 )
601 .unwrap()
602 .unwrap();
603 let obj = match &value {
604 JsonValue::Object(o) => o,
605 _ => panic!("Expected object"),
606 };
607 assert_eq!(obj.get_number("$").unwrap(), "1");
608 assert_eq!(obj.get_number("_$_").unwrap(), "2");
609 assert_eq!(obj.get_number("$_$hello123").unwrap(), "3");
610 assert_eq!(obj.get_number("NaN$").unwrap(), "4");
611 assert_eq!(obj.get_number("true$").unwrap(), "5");
612 }
613
614 #[test]
615 fn it_should_parse_loose_property_names_starting_with_infinity_or_nan() {
616 let value = parse_to_value(r#"{ Infinity_count: 1, NaN-key: 2 }"#, &Default::default())
617 .unwrap()
618 .unwrap();
619 let obj = match &value {
620 JsonValue::Object(o) => o,
621 _ => panic!("Expected object"),
622 };
623 assert_eq!(obj.get_number("Infinity_count").unwrap(), "1");
624 assert_eq!(obj.get_number("NaN-key").unwrap(), "2");
625 }
626
627 #[test]
628 fn it_should_keep_keyword_boundaries_with_missing_commas() {
629 let value = parse_to_value("[true-1]", &Default::default()).unwrap().unwrap();
630 let arr = match value {
631 JsonValue::Array(a) => a,
632 _ => panic!("Expected array"),
633 };
634 assert_eq!(arr.len(), 2);
635 assert!(matches!(arr.get(0), Some(JsonValue::Boolean(true))));
636 assert!(matches!(arr.get(1), Some(JsonValue::Number("-1"))));
637 }
638
639 #[test]
640 fn it_should_parse_unary_plus_numbers() {
641 let value = parse_to_value(r#"{ "test": +42 }"#, &Default::default())
642 .unwrap()
643 .unwrap();
644 let obj = match &value {
645 JsonValue::Object(o) => o,
646 _ => panic!("Expected object"),
647 };
648 assert_eq!(obj.get_number("test").unwrap(), "+42");
649 }
650
651 #[test]
652 fn it_should_parse_large_shallow_objects() {
653 let mut json = "{\"q\":[".to_string();
655 let size = 1_000;
656
657 for _ in 0..size {
658 json += "{\"q\":[{}]}, [\"hello\"], ";
659 }
660
661 json += "]}";
662
663 let result = parse_to_value(&json, &ParseOptions::default());
664 assert!(result.is_ok());
665 }
666}