1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::rc::Rc;
4
5use super::ast::*;
6use super::common::Range;
7use super::errors::*;
8use super::scanner::Scanner;
9use super::scanner::ScannerOptions;
10use super::tokens::Token;
11use super::tokens::TokenAndRange;
12
13pub type CommentMap<'a> = HashMap<usize, Rc<Vec<Comment<'a>>>>;
17
18#[derive(Default, Debug, PartialEq, Clone)]
23pub enum CommentCollectionStrategy {
24 #[default]
26 Off,
27 Separate,
33 AsTokens,
38}
39
40#[derive(Default, Clone)]
42pub struct CollectOptions {
43 pub comments: CommentCollectionStrategy,
45 pub tokens: bool,
47}
48
49#[derive(Clone)]
51pub struct ParseOptions {
52 pub allow_comments: bool,
54 pub allow_loose_object_property_names: bool,
56 pub allow_trailing_commas: bool,
58 pub allow_missing_commas: bool,
60 pub allow_single_quoted_strings: bool,
62 pub allow_hexadecimal_numbers: bool,
64 pub allow_unary_plus_numbers: bool,
66 pub allow_bare_decimal_point_numbers: bool,
68 pub allow_non_finite_numbers: bool,
70 pub allow_extended_string_escapes: bool,
72}
73
74impl Default for ParseOptions {
75 fn default() -> Self {
76 Self {
77 allow_comments: true,
78 allow_loose_object_property_names: true,
79 allow_trailing_commas: true,
80 allow_missing_commas: true,
81 allow_single_quoted_strings: true,
82 allow_hexadecimal_numbers: true,
83 allow_unary_plus_numbers: true,
84 allow_bare_decimal_point_numbers: true,
85 allow_non_finite_numbers: true,
86 allow_extended_string_escapes: true,
87 }
88 }
89}
90
91pub struct ParseResult<'a> {
93 pub comments: Option<CommentMap<'a>>,
99 pub value: Option<Value<'a>>,
101 pub tokens: Option<Vec<TokenAndRange<'a>>>,
105}
106
107struct Context<'a> {
108 scanner: Scanner<'a>,
109 comments: Option<CommentMap<'a>>,
110 current_comments: Option<Vec<Comment<'a>>>,
111 last_token_end: usize,
112 range_stack: Vec<Range>,
113 tokens: Option<Vec<TokenAndRange<'a>>>,
114 collect_comments_as_tokens: bool,
115 allow_comments: bool,
116 allow_trailing_commas: bool,
117 allow_missing_commas: bool,
118 allow_loose_object_property_names: bool,
119 maximum_nesting_depth: usize,
120}
121
122impl<'a> Context<'a> {
123 pub fn scan(&mut self) -> Result<Option<Token<'a>>, ParseError> {
124 let previous_end = self.last_token_end;
125 let token = self.scan_handling_comments()?;
126 self.last_token_end = self.scanner.token_end();
127
128 if let Some(comments) = self.comments.as_mut()
130 && let Some(current_comments) = self.current_comments.take()
131 {
132 let current_comments = Rc::new(current_comments);
133 comments.insert(previous_end, current_comments.clone());
134 comments.insert(self.scanner.token_start(), current_comments);
135 }
136
137 if let Some(token) = &token
138 && self.tokens.is_some()
139 {
140 self.capture_token(token.clone());
141 }
142
143 Ok(token)
144 }
145
146 pub fn token(&self) -> Option<Token<'a>> {
147 self.scanner.token()
148 }
149
150 pub fn start_range(&mut self) {
151 self.range_stack.push(Range {
152 start: self.scanner.token_start(),
153 end: 0,
154 });
155 }
156
157 pub fn end_range(&mut self) -> Range {
158 let mut range = self
159 .range_stack
160 .pop()
161 .expect("Range was popped from the stack, but the stack was empty.");
162 range.end = self.scanner.token_end();
163 range
164 }
165
166 pub fn create_range_from_last_token(&self) -> Range {
167 Range {
168 start: self.scanner.token_start(),
169 end: self.scanner.token_end(),
170 }
171 }
172
173 pub fn create_error(&self, kind: ParseErrorKind) -> ParseError {
174 self.scanner.create_error_for_current_token(kind)
175 }
176
177 pub fn create_error_for_current_range(&mut self, kind: ParseErrorKind) -> ParseError {
178 let range = self.end_range();
179 self.create_error_for_range(range, kind)
180 }
181
182 pub fn create_error_for_range(&self, range: Range, kind: ParseErrorKind) -> ParseError {
183 self.scanner.create_error_for_range(range, kind)
184 }
185
186 fn scan_handling_comments(&mut self) -> Result<Option<Token<'a>>, ParseError> {
187 loop {
188 let token = self.scanner.scan()?;
189 match token {
190 Some(token @ Token::CommentLine(_) | token @ Token::CommentBlock(_)) if self.collect_comments_as_tokens => {
191 self.capture_token(token);
192 }
193 Some(Token::CommentLine(text)) => {
194 self.handle_comment(Comment::Line(CommentLine {
195 range: self.create_range_from_last_token(),
196 text,
197 }))?;
198 }
199 Some(Token::CommentBlock(text)) => {
200 self.handle_comment(Comment::Block(CommentBlock {
201 range: self.create_range_from_last_token(),
202 text,
203 }))?;
204 }
205 _ => return Ok(token),
206 }
207 }
208 }
209
210 fn capture_token(&mut self, token: Token<'a>) {
211 let range = self.create_range_from_last_token();
212 if let Some(tokens) = self.tokens.as_mut() {
213 tokens.push(TokenAndRange {
214 token: token.clone(),
215 range,
216 });
217 }
218 }
219
220 fn handle_comment(&mut self, comment: Comment<'a>) -> Result<(), ParseError> {
221 if !self.allow_comments {
222 return Err(self.create_error(ParseErrorKind::CommentsNotAllowed));
223 }
224
225 if self.comments.is_some() {
226 if let Some(comments) = self.current_comments.as_mut() {
227 comments.push(comment);
228 } else {
229 self.current_comments = Some(vec![comment]);
230 }
231 }
232
233 Ok(())
234 }
235}
236
237pub fn parse_to_ast<'a>(
254 text: &'a str,
255 collect_options: &CollectOptions,
256 parse_options: &ParseOptions,
257) -> Result<ParseResult<'a>, ParseError> {
258 let mut context = Context {
259 scanner: Scanner::new(
260 text,
261 &ScannerOptions {
262 allow_single_quoted_strings: parse_options.allow_single_quoted_strings,
263 allow_hexadecimal_numbers: parse_options.allow_hexadecimal_numbers,
264 allow_unary_plus_numbers: parse_options.allow_unary_plus_numbers,
265 allow_bare_decimal_point_numbers: parse_options.allow_bare_decimal_point_numbers,
266 allow_non_finite_numbers: parse_options.allow_non_finite_numbers,
267 allow_extended_string_escapes: parse_options.allow_extended_string_escapes,
268 },
269 ),
270 comments: match collect_options.comments {
271 CommentCollectionStrategy::Separate => Some(Default::default()),
272 CommentCollectionStrategy::Off | CommentCollectionStrategy::AsTokens => None,
273 },
274 current_comments: None,
275 last_token_end: 0,
276 range_stack: Vec::new(),
277 tokens: if collect_options.tokens { Some(Vec::new()) } else { None },
278 collect_comments_as_tokens: collect_options.comments == CommentCollectionStrategy::AsTokens,
279 allow_comments: parse_options.allow_comments,
280 allow_trailing_commas: parse_options.allow_trailing_commas,
281 allow_missing_commas: parse_options.allow_missing_commas,
282 allow_loose_object_property_names: parse_options.allow_loose_object_property_names,
283 maximum_nesting_depth: 512,
284 };
285 context.scan()?;
286 let value = parse_value(&mut context)?;
287
288 if context.scan()?.is_some() {
289 return Err(context.create_error(ParseErrorKind::MultipleRootJsonValues));
290 }
291
292 debug_assert!(context.range_stack.is_empty());
293
294 Ok(ParseResult {
295 comments: context.comments,
296 tokens: context.tokens,
297 value,
298 })
299}
300
301fn parse_value<'a>(context: &mut Context<'a>) -> Result<Option<Value<'a>>, ParseError> {
302 if context.range_stack.len() > context.maximum_nesting_depth {
303 return Err(context.create_error_for_current_range(ParseErrorKind::NestingDepthExceeded));
304 }
305
306 match context.token() {
307 None => Ok(None),
308 Some(token) => match token {
309 Token::OpenBrace => Ok(Some(Value::Object(parse_object(context)?))),
310 Token::OpenBracket => Ok(Some(Value::Array(parse_array(context)?))),
311 Token::String(value) => Ok(Some(Value::StringLit(create_string_lit(context, value)))),
312 Token::Boolean(value) => Ok(Some(Value::BooleanLit(create_boolean_lit(context, value)))),
313 Token::Number(value) => Ok(Some(Value::NumberLit(create_number_lit(context, value)))),
314 Token::Null => Ok(Some(Value::NullKeyword(create_null_keyword(context)))),
315 Token::CloseBracket => Err(context.create_error(ParseErrorKind::UnexpectedCloseBracket)),
316 Token::CloseBrace => Err(context.create_error(ParseErrorKind::UnexpectedCloseBrace)),
317 Token::Comma => Err(context.create_error(ParseErrorKind::UnexpectedComma)),
318 Token::Colon => Err(context.create_error(ParseErrorKind::UnexpectedColon)),
319 Token::Word(_) => Err(context.create_error(ParseErrorKind::UnexpectedWord)),
320 Token::CommentLine(_) => unreachable!(),
321 Token::CommentBlock(_) => unreachable!(),
322 },
323 }
324}
325
326fn parse_object<'a>(context: &mut Context<'a>) -> Result<Object<'a>, ParseError> {
327 debug_assert!(context.token() == Some(Token::OpenBrace));
328 let mut properties = Vec::new();
329
330 context.start_range();
331 context.scan()?;
332
333 loop {
334 match context.token() {
335 Some(Token::CloseBrace) => break,
336 Some(Token::String(prop_name)) => {
337 properties.push(parse_object_property(context, PropName::String(prop_name))?);
338 }
339 None => return Err(context.create_error_for_current_range(ParseErrorKind::UnterminatedObject)),
340 Some(token) => match token.as_loose_property_name() {
341 Some(prop_name) => properties.push(parse_object_property(context, PropName::Word(prop_name))?),
342 None => return Err(context.create_error(ParseErrorKind::UnexpectedTokenInObject)),
343 },
344 }
345
346 let after_value_end = context.last_token_end;
348 match context.scan()? {
349 Some(Token::Comma) => {
350 let comma_range = context.create_range_from_last_token();
351 if let Some(Token::CloseBrace) = context.scan()?
352 && !context.allow_trailing_commas
353 {
354 return Err(context.create_error_for_range(comma_range, ParseErrorKind::TrailingCommasNotAllowed));
355 }
356 }
357 Some(Token::String(_) | Token::Word(_) | Token::Number(_) | Token::Boolean(_) | Token::Null)
358 if !context.allow_missing_commas =>
359 {
360 let range = Range {
361 start: after_value_end,
362 end: after_value_end,
363 };
364 return Err(context.create_error_for_range(range, ParseErrorKind::ExpectedComma));
365 }
366 _ => {}
367 }
368 }
369
370 Ok(Object {
371 range: context.end_range(),
372 properties,
373 })
374}
375
376enum PropName<'a> {
377 String(Cow<'a, str>),
378 Word(&'a str),
379}
380
381fn parse_object_property<'a>(context: &mut Context<'a>, prop_name: PropName<'a>) -> Result<ObjectProp<'a>, ParseError> {
382 context.start_range();
383
384 let name = match prop_name {
385 PropName::String(prop_name) => ObjectPropName::String(create_string_lit(context, prop_name)),
386 PropName::Word(prop_name) => {
387 if context.allow_loose_object_property_names {
388 ObjectPropName::Word(create_word(context, prop_name))
389 } else {
390 return Err(context.create_error(ParseErrorKind::ExpectedStringObjectProperty));
391 }
392 }
393 };
394
395 match context.scan()? {
396 Some(Token::Colon) => {}
397 _ => return Err(context.create_error(ParseErrorKind::ExpectedColonAfterObjectKey)),
398 }
399
400 context.scan()?;
401 let value = parse_value(context)?;
402
403 match value {
404 Some(value) => Ok(ObjectProp {
405 range: context.end_range(),
406 name,
407 value,
408 }),
409 None => Err(context.create_error(ParseErrorKind::ExpectedObjectValue)),
410 }
411}
412
413fn parse_array<'a>(context: &mut Context<'a>) -> Result<Array<'a>, ParseError> {
414 debug_assert!(context.token() == Some(Token::OpenBracket));
415 let mut elements = Vec::new();
416
417 context.start_range();
418 context.scan()?;
419
420 loop {
421 match context.token() {
422 Some(Token::CloseBracket) => break,
423 None => return Err(context.create_error_for_current_range(ParseErrorKind::UnterminatedArray)),
424 _ => match parse_value(context)? {
425 Some(value) => elements.push(value),
426 None => return Err(context.create_error_for_current_range(ParseErrorKind::UnterminatedArray)),
427 },
428 }
429
430 let after_value_end = context.last_token_end;
432 match context.scan()? {
433 Some(Token::Comma) => {
434 let comma_range = context.create_range_from_last_token();
435 if let Some(Token::CloseBracket) = context.scan()?
436 && !context.allow_trailing_commas
437 {
438 return Err(context.create_error_for_range(comma_range, ParseErrorKind::TrailingCommasNotAllowed));
439 }
440 }
441 Some(token) if !context.allow_missing_commas && token.is_value_start() => {
442 let range = Range {
443 start: after_value_end,
444 end: after_value_end,
445 };
446 return Err(context.create_error_for_range(range, ParseErrorKind::ExpectedComma));
447 }
448 _ => {}
449 }
450 }
451
452 Ok(Array {
453 range: context.end_range(),
454 elements,
455 })
456}
457
458fn create_string_lit<'a>(context: &Context<'a>, value: Cow<'a, str>) -> StringLit<'a> {
461 StringLit {
462 range: context.create_range_from_last_token(),
463 value,
464 }
465}
466
467fn create_word<'a>(context: &Context<'a>, value: &'a str) -> WordLit<'a> {
468 WordLit {
469 range: context.create_range_from_last_token(),
470 value,
471 }
472}
473
474fn create_boolean_lit(context: &Context, value: bool) -> BooleanLit {
475 BooleanLit {
476 range: context.create_range_from_last_token(),
477 value,
478 }
479}
480
481fn create_number_lit<'a>(context: &Context<'a>, value: &'a str) -> NumberLit<'a> {
482 NumberLit {
483 range: context.create_range_from_last_token(),
484 value,
485 }
486}
487
488fn create_null_keyword(context: &Context) -> NullKeyword {
489 NullKeyword {
490 range: context.create_range_from_last_token(),
491 }
492}
493
494#[cfg(test)]
495mod tests {
496 use super::*;
497 use pretty_assertions::assert_eq;
498
499 #[test]
500 fn it_should_error_when_has_multiple_values() {
501 assert_has_error(
502 "[][]",
503 "Text cannot contain more than one JSON value on line 1 column 3",
504 );
505 }
506
507 #[test]
508 fn it_should_error_when_object_is_not_terminated() {
509 assert_has_error("{", "Unterminated object on line 1 column 1");
510 }
511
512 #[test]
513 fn it_should_error_when_object_has_unexpected_token() {
514 assert_has_error("{ [] }", "Unexpected token in object on line 1 column 3");
515 }
516
517 #[test]
518 fn it_should_error_when_object_has_two_non_string_tokens() {
519 assert_has_error(
520 "{ asdf asdf: 5 }",
521 "Expected colon after the string or word in object property on line 1 column 8",
522 );
523 }
524
525 #[test]
526 fn it_should_error_when_array_is_not_terminated() {
527 assert_has_error("[", "Unterminated array on line 1 column 1");
528 }
529
530 #[test]
531 fn it_should_error_when_array_has_unexpected_token() {
532 assert_has_error("[:]", "Unexpected colon on line 1 column 2");
533 }
534
535 #[test]
536 fn it_should_error_when_comment_block_not_closed() {
537 assert_has_error("/* test", "Unterminated comment block on line 1 column 1");
538 }
539
540 #[test]
541 fn it_should_error_when_string_lit_not_closed() {
542 assert_has_error("\" test", "Unterminated string literal on line 1 column 1");
543 }
544
545 fn assert_has_error(text: &str, message: &str) {
546 let result = parse_to_ast(text, &Default::default(), &Default::default());
547 match result {
548 Ok(_) => panic!("Expected error, but did not find one."),
549 Err(err) => assert_eq!(err.to_string(), message),
550 }
551 }
552
553 #[test]
554 fn strict_should_error_object_trailing_comma() {
555 assert_has_strict_error(
556 r#"{ "test": 5, }"#,
557 "Trailing commas are not allowed on line 1 column 12",
558 );
559 }
560
561 #[test]
562 fn strict_should_error_array_trailing_comma() {
563 assert_has_strict_error(r#"[ "test", ]"#, "Trailing commas are not allowed on line 1 column 9");
564 }
565
566 #[test]
567 fn strict_should_error_comment_line() {
568 assert_has_strict_error(r#"[ "test" ] // 1"#, "Comments are not allowed on line 1 column 12");
569 }
570
571 #[test]
572 fn strict_should_error_comment_block() {
573 assert_has_strict_error(r#"[ "test" /* 1 */]"#, "Comments are not allowed on line 1 column 10");
574 }
575
576 #[test]
577 fn strict_should_error_word_property() {
578 assert_has_strict_error(
579 r#"{ word: 5 }"#,
580 "Expected string for object property on line 1 column 3",
581 );
582 assert_has_strict_error(
583 r#"{ true: 5 }"#,
584 "Expected string for object property on line 1 column 3",
585 );
586 assert_has_strict_error(r#"{ "a": 1 null: 2 }"#, "Expected comma on line 1 column 9");
587 }
588
589 #[test]
590 fn strict_should_error_single_quoted_string() {
591 assert_has_strict_error(
592 r#"{ "key": 'value' }"#,
593 "Single-quoted strings are not allowed on line 1 column 10",
594 );
595 }
596
597 #[test]
598 fn strict_should_error_hexadecimal_number() {
599 assert_has_strict_error(
600 r#"{ "key": 0xFF }"#,
601 "Hexadecimal numbers are not allowed on line 1 column 10",
602 );
603 }
604
605 #[test]
606 fn strict_should_error_unary_plus_number() {
607 assert_has_strict_error(
608 r#"{ "key": +42 }"#,
609 "Unary plus on numbers is not allowed on line 1 column 10",
610 );
611 }
612
613 #[test]
614 fn strict_should_error_bare_decimal_point_number() {
615 assert_has_strict_error(
616 r#"{ "key": .5 }"#,
617 "Leading or trailing decimal points on numbers are not allowed on line 1 column 10",
618 );
619 assert_has_strict_error(
620 r#"{ "key": 5. }"#,
621 "Leading or trailing decimal points on numbers are not allowed on line 1 column 10",
622 );
623 }
624
625 #[test]
626 fn strict_should_error_non_finite_number() {
627 assert_has_strict_error(
628 r#"{ "key": -Infinity }"#,
629 "Infinity and NaN are not allowed on line 1 column 10",
630 );
631 assert_has_strict_error(r#"{ "key": NaN }"#, "Unexpected word on line 1 column 10");
632 }
633
634 #[track_caller]
635 fn assert_has_strict_error(text: &str, message: &str) {
636 let result = parse_to_ast(text, &Default::default(), &strict_options());
637 match result {
638 Ok(_) => panic!("Expected error, but did not find one."),
639 Err(err) => assert_eq!(err.to_string(), message),
640 }
641 }
642
643 fn strict_options() -> ParseOptions {
644 ParseOptions {
645 allow_comments: false,
646 allow_loose_object_property_names: false,
647 allow_trailing_commas: false,
648 allow_missing_commas: false,
649 allow_single_quoted_strings: false,
650 allow_hexadecimal_numbers: false,
651 allow_unary_plus_numbers: false,
652 allow_bare_decimal_point_numbers: false,
653 allow_non_finite_numbers: false,
654 allow_extended_string_escapes: false,
655 }
656 }
657
658 #[test]
659 fn it_should_not_include_tokens_by_default() {
660 let result = parse_to_ast("{}", &Default::default(), &Default::default()).unwrap();
661 assert!(result.tokens.is_none());
662 }
663
664 #[test]
665 fn it_should_include_tokens_when_specified() {
666 let result = parse_to_ast(
667 "{}",
668 &CollectOptions {
669 tokens: true,
670 ..Default::default()
671 },
672 &Default::default(),
673 )
674 .unwrap();
675 let tokens = result.tokens.unwrap();
676 assert_eq!(tokens.len(), 2);
677 }
678
679 #[test]
680 fn it_should_not_include_comments_by_default() {
681 let result = parse_to_ast("{}", &Default::default(), &Default::default()).unwrap();
682 assert!(result.comments.is_none());
683 }
684
685 #[test]
686 fn it_should_include_comments_when_specified() {
687 let result = parse_to_ast(
688 "{} // 2",
689 &CollectOptions {
690 comments: CommentCollectionStrategy::Separate,
691 ..Default::default()
692 },
693 &Default::default(),
694 )
695 .unwrap();
696 let comments = result.comments.unwrap();
697 assert_eq!(comments.len(), 2); }
699
700 #[cfg(not(feature = "error_unicode_width"))]
701 #[test]
702 fn error_correct_line_column_unicode_width() {
703 assert_has_strict_error(r#"["🧑🦰", ["#, "Unterminated array on line 1 column 9");
704 }
705
706 #[cfg(feature = "error_unicode_width")]
707 #[test]
708 fn error_correct_line_column_unicode_width() {
709 assert_has_strict_error(r#"["🧑🦰", ["#, "Unterminated array on line 1 column 10");
710 }
711
712 #[test]
713 fn it_should_parse_unquoted_keys_with_hex_and_trailing_comma() {
714 let text = r#"{
715 CP_CanFuncReqId: 0x7DF, // 2015
716 }"#;
717 {
718 let parse_result = parse_to_ast(text, &Default::default(), &Default::default()).unwrap();
719
720 let value = parse_result.value.unwrap();
721 let obj = value.as_object().unwrap();
722 assert_eq!(obj.properties.len(), 1);
723 assert_eq!(obj.properties[0].name.as_str(), "CP_CanFuncReqId");
724
725 let number_value = obj.properties[0].value.as_number_lit().unwrap();
726 assert_eq!(number_value.value, "0x7DF");
727 }
728 #[cfg(feature = "serde")]
729 {
730 let value: serde_json::Value = crate::parse_to_serde_value(text, &Default::default()).unwrap();
731 assert_eq!(
733 value,
734 serde_json::json!({
735 "CP_CanFuncReqId": 2015
736 })
737 );
738 }
739 }
740
741 #[test]
742 fn it_should_parse_unary_plus_numbers() {
743 let result = parse_to_ast(r#"{ "test": +42 }"#, &Default::default(), &Default::default()).unwrap();
744
745 let value = result.value.unwrap();
746 let obj = value.as_object().unwrap();
747 assert_eq!(obj.properties.len(), 1);
748 assert_eq!(obj.properties[0].name.as_str(), "test");
749
750 let number_value = obj.properties[0].value.as_number_lit().unwrap();
751 assert_eq!(number_value.value, "+42");
752 }
753
754 #[test]
755 fn it_should_parse_keywords_as_loose_property_names() {
756 let result = parse_to_ast(
757 r#"{ true: 1, false: null null: true }"#,
758 &Default::default(),
759 &Default::default(),
760 )
761 .unwrap();
762 let value = result.value.unwrap();
763 let names = value
764 .as_object()
765 .unwrap()
766 .properties
767 .iter()
768 .map(|p| p.name.as_str())
769 .collect::<Vec<_>>();
770 assert_eq!(names, vec!["true", "false", "null"]);
771 }
772
773 #[test]
774 fn it_should_parse_json5_numbers_and_keep_non_finite_words_as_keys() {
775 let result = parse_to_ast(
776 r#"{ "a": .5, "b": 5., "c": -Infinity, Infinity: NaN }"#,
777 &Default::default(),
778 &Default::default(),
779 )
780 .unwrap();
781
782 let value = result.value.unwrap();
783 let obj = value.as_object().unwrap();
784 let values = obj
785 .properties
786 .iter()
787 .map(|p| (p.name.as_str(), p.value.as_number_lit().unwrap().value))
788 .collect::<Vec<_>>();
789 assert_eq!(
790 values,
791 vec![("a", ".5"), ("b", "5."), ("c", "-Infinity"), ("Infinity", "NaN")]
792 );
793 }
794
795 #[test]
796 fn missing_comma_between_properties() {
797 let text = r#"{
798 "name": "alice"
799 "age": 25
800}"#;
801 let result = parse_to_ast(text, &Default::default(), &Default::default()).unwrap();
802 assert_eq!(
803 result
804 .value
805 .unwrap()
806 .as_object()
807 .unwrap()
808 .get_number("age")
809 .unwrap()
810 .value,
811 "25"
812 );
813
814 assert_has_strict_error(text, "Expected comma on line 2 column 18");
816 }
817
818 #[test]
819 fn missing_comma_with_comment_between_properties() {
820 let result = parse_to_ast(
823 r#"{
824 "name": "alice" // comment here
825 "age": 25
826}"#,
827 &Default::default(),
828 &ParseOptions {
829 allow_comments: true,
830 allow_missing_commas: false,
831 ..Default::default()
832 },
833 );
834 match result {
835 Ok(_) => panic!("Expected error, but did not find one."),
836 Err(err) => assert_eq!(err.to_string(), "Expected comma on line 2 column 18"),
837 }
838 }
839
840 #[test]
841 fn missing_comma_between_array_elements() {
842 let text = "[1 2]";
843 let result = parse_to_ast(text, &Default::default(), &Default::default()).unwrap();
844 let value = result.value.unwrap();
845 let elements = &value.as_array().unwrap().elements;
846 assert_eq!(elements.len(), 2);
847
848 assert_has_strict_error(text, "Expected comma on line 1 column 3");
850 assert_has_strict_error("[01]", "Expected comma on line 1 column 3");
851 assert_has_strict_error(r#"["a" "b"]"#, "Expected comma on line 1 column 5");
852 assert_has_strict_error("[true false]", "Expected comma on line 1 column 6");
853 assert_has_strict_error("[null null]", "Expected comma on line 1 column 6");
854 assert_has_strict_error("[[1] [2]]", "Expected comma on line 1 column 5");
855 assert_has_strict_error(r#"[{"a":1} {"b":2}]"#, "Expected comma on line 1 column 9");
856 assert_has_strict_error("[1 2", "Expected comma on line 1 column 3");
857
858 assert_has_strict_error("[1", "Unterminated array on line 1 column 1");
860 assert_has_strict_error("[1 a ]", "Unexpected word on line 1 column 4");
861
862 for text in ["[]", "[ ]", "[1,2]", "[1 , 2]", "[[1],[2]]", r#"[{"a":1},{"b":2}]"#] {
863 parse_to_ast(text, &Default::default(), &strict_options()).unwrap();
864 }
865 }
866
867 #[test]
868 fn missing_comma_not_allowed_with_trailing_commas_allowed_in_array() {
869 let options = ParseOptions {
870 allow_missing_commas: false,
871 allow_trailing_commas: true,
872 ..Default::default()
873 };
874 for text in ["[]", "[1,]", "[1, 2,]", "[[1,],[2,],]"] {
875 parse_to_ast(text, &Default::default(), &options).unwrap();
876 }
877 }
878
879 #[test]
880 fn missing_comma_with_comment_between_array_elements() {
881 let result = parse_to_ast(
884 r#"[
885 1 // comment here
886 2
887]"#,
888 &Default::default(),
889 &ParseOptions {
890 allow_comments: true,
891 allow_missing_commas: false,
892 ..Default::default()
893 },
894 );
895 match result {
896 Ok(_) => panic!("Expected error, but did not find one."),
897 Err(err) => assert_eq!(err.to_string(), "Expected comma on line 2 column 4"),
898 }
899 }
900
901 #[test]
902 fn it_should_error_when_arrays_are_deeply_nested() {
903 let mut json = String::new();
905 let depth = 30_000;
906
907 for _ in 0..depth {
908 json += "[";
909 }
910
911 for _ in 0..depth {
912 json += "]";
913 }
914
915 let result = parse_to_ast(&json, &Default::default(), &ParseOptions::default());
916
917 match result {
918 Ok(_) => panic!("Expected error, but did not find one."),
919 Err(err) => assert_eq!(err.to_string(), "Maximum nesting depth exceeded on line 1 column 513"),
920 }
921 }
922
923 #[test]
924 fn it_should_error_when_objects_are_deeply_nested() {
925 let mut json = String::new();
927 let depth = 30_000;
928
929 for _ in 0..depth {
930 json += "{\"q\":";
931 }
932
933 for _ in 0..depth {
934 json += "}";
935 }
936
937 let result = parse_to_ast(&json, &Default::default(), &ParseOptions::default());
938
939 match result {
940 Ok(_) => panic!("Expected error, but did not find one."),
941 Err(err) => assert_eq!(err.to_string(), "Maximum nesting depth exceeded on line 1 column 1282"),
942 }
943 }
944
945 #[test]
946 fn it_should_parse_large_shallow_objects() {
947 let mut json = "{\"q\":[".to_string();
949 let size = 1_000;
950
951 for _ in 0..size {
952 json += "{\"q\":[{}]}, [\"hello\"], ";
953 }
954
955 json += "]}";
956
957 let result = parse_to_ast(&json, &Default::default(), &ParseOptions::default());
958
959 match result {
960 Ok(_) => {}
961 Err(_) => panic!("Expected Ok, but did not find one."),
962 }
963 }
964}