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 }
363 }
364
365 #[test]
366 fn it_should_error_when_has_multiple_values() {
367 assert_has_error(
368 "[][]",
369 "Text cannot contain more than one JSON value on line 1 column 3",
370 );
371 }
372
373 #[test]
374 fn it_should_error_when_object_is_not_terminated() {
375 assert_has_error("{", "Unterminated object on line 1 column 2");
376 }
377
378 #[test]
379 fn it_should_error_when_object_has_unexpected_token() {
380 assert_has_error("{ [] }", "Unexpected token in object on line 1 column 3");
381 }
382
383 #[test]
384 fn it_should_error_when_object_has_two_non_string_tokens() {
385 assert_has_error(
386 "{ asdf asdf: 5 }",
387 "Expected colon after the string or word in object property on line 1 column 8",
388 );
389 }
390
391 #[test]
392 fn it_should_error_when_array_is_not_terminated() {
393 assert_has_error("[", "Unterminated array on line 1 column 2");
394 }
395
396 #[test]
397 fn it_should_error_when_array_has_unexpected_token() {
398 assert_has_error("[:]", "Unexpected colon on line 1 column 2");
399 }
400
401 #[test]
402 fn it_should_error_when_comment_block_not_closed() {
403 assert_has_error("/* test", "Unterminated comment block on line 1 column 1");
404 }
405
406 #[test]
407 fn it_should_error_when_string_lit_not_closed() {
408 assert_has_error("\" test", "Unterminated string literal on line 1 column 1");
409 }
410
411 #[test]
412 fn strict_should_error_object_trailing_comma() {
413 assert_has_strict_error(
414 r#"{ "test": 5, }"#,
415 "Trailing commas are not allowed on line 1 column 12",
416 );
417 }
418
419 #[test]
420 fn strict_should_error_array_trailing_comma() {
421 assert_has_strict_error(r#"[ "test", ]"#, "Trailing commas are not allowed on line 1 column 9");
422 }
423
424 #[test]
425 fn strict_should_error_comment_line() {
426 assert_has_strict_error(r#"[ "test" ] // 1"#, "Comments are not allowed on line 1 column 12");
427 }
428
429 #[test]
430 fn strict_should_error_comment_block() {
431 assert_has_strict_error(r#"[ "test" /* 1 */]"#, "Comments are not allowed on line 1 column 10");
432 }
433
434 #[test]
435 fn strict_should_error_word_property() {
436 assert_has_strict_error(
437 r#"{ word: 5 }"#,
438 "Expected string for object property on line 1 column 3",
439 );
440 }
441
442 #[test]
443 fn strict_should_error_single_quoted_string() {
444 assert_has_strict_error(
445 r#"{ "key": 'value' }"#,
446 "Single-quoted strings are not allowed on line 1 column 10",
447 );
448 }
449
450 #[test]
451 fn strict_should_error_hexadecimal_number() {
452 assert_has_strict_error(
453 r#"{ "key": 0xFF }"#,
454 "Hexadecimal numbers are not allowed on line 1 column 10",
455 );
456 }
457
458 #[test]
459 fn strict_should_error_unary_plus_number() {
460 assert_has_strict_error(
461 r#"{ "key": +42 }"#,
462 "Unary plus on numbers is not allowed on line 1 column 10",
463 );
464 }
465
466 #[test]
467 fn missing_comma_between_properties() {
468 let text = r#"{
469 "name": "alice"
470 "age": 25
471}"#;
472 let value = parse_to_value(text, &Default::default()).unwrap().unwrap();
473 let obj = match &value {
474 JsonValue::Object(o) => o,
475 _ => panic!("Expected object"),
476 };
477 assert_eq!(obj.get_number("age").unwrap(), "25");
478
479 assert_has_strict_error(text, "Expected comma on line 2 column 18");
481 }
482
483 #[test]
484 fn missing_comma_with_comment_between_properties() {
485 let result = parse_to_value(
488 r#"{
489 "name": "alice" // comment here
490 "age": 25
491}"#,
492 &ParseOptions {
493 allow_comments: true,
494 allow_missing_commas: false,
495 ..Default::default()
496 },
497 );
498 match result {
499 Ok(_) => panic!("Expected error, but did not find one."),
500 Err(err) => assert_eq!(err.to_string(), "Expected comma on line 2 column 18"),
501 }
502 }
503
504 #[test]
505 fn missing_comma_between_array_elements() {
506 let text = "[1 2]";
507 let value = parse_to_value(text, &Default::default()).unwrap().unwrap();
508 assert_eq!(
509 value,
510 JsonValue::Array(vec![JsonValue::Number("1"), JsonValue::Number("2")].into())
511 );
512
513 assert_has_strict_error(text, "Expected comma on line 1 column 3");
515 assert_has_strict_error("[01]", "Expected comma on line 1 column 3");
516 assert_has_strict_error(r#"["a" "b"]"#, "Expected comma on line 1 column 5");
517 assert_has_strict_error("[true false]", "Expected comma on line 1 column 6");
518 assert_has_strict_error("[null null]", "Expected comma on line 1 column 6");
519 assert_has_strict_error("[[1] [2]]", "Expected comma on line 1 column 5");
520 assert_has_strict_error(r#"[{"a":1} {"b":2}]"#, "Expected comma on line 1 column 9");
521 assert_has_strict_error("[1 2", "Expected comma on line 1 column 3");
522
523 assert_has_strict_error("[1", "Unterminated array on line 1 column 3");
525 assert_has_strict_error("[1 a ]", "Unexpected word on line 1 column 4");
526
527 for text in ["[]", "[ ]", "[1,2]", "[1 , 2]", "[[1],[2]]", r#"[{"a":1},{"b":2}]"#] {
528 parse_to_value(text, &strict_options()).unwrap();
529 }
530 }
531
532 #[test]
533 fn missing_comma_not_allowed_with_trailing_commas_allowed_in_array() {
534 let options = ParseOptions {
535 allow_missing_commas: false,
536 allow_trailing_commas: true,
537 ..Default::default()
538 };
539 for text in ["[]", "[1,]", "[1, 2,]", "[[1,],[2,],]"] {
540 parse_to_value(text, &options).unwrap();
541 }
542 }
543
544 #[test]
545 fn missing_comma_with_comment_between_array_elements() {
546 let result = parse_to_value(
549 r#"[
550 1 // comment here
551 2
552]"#,
553 &ParseOptions {
554 allow_comments: true,
555 allow_missing_commas: false,
556 ..Default::default()
557 },
558 );
559 match result {
560 Ok(_) => panic!("Expected error, but did not find one."),
561 Err(err) => assert_eq!(err.to_string(), "Expected comma on line 2 column 4"),
562 }
563 }
564
565 #[test]
566 fn it_should_parse_unquoted_keys_with_hex_and_trailing_comma() {
567 let text = r#"{
568 CP_CanFuncReqId: 0x7DF, // 2015
569 }"#;
570 let value = parse_to_value(text, &Default::default()).unwrap().unwrap();
571 let obj = match &value {
572 JsonValue::Object(o) => o,
573 _ => panic!("Expected object"),
574 };
575 assert_eq!(obj.get_number("CP_CanFuncReqId").unwrap(), "0x7DF");
576 }
577
578 #[test]
579 fn it_should_parse_unary_plus_numbers() {
580 let value = parse_to_value(r#"{ "test": +42 }"#, &Default::default())
581 .unwrap()
582 .unwrap();
583 let obj = match &value {
584 JsonValue::Object(o) => o,
585 _ => panic!("Expected object"),
586 };
587 assert_eq!(obj.get_number("test").unwrap(), "+42");
588 }
589
590 #[test]
591 fn it_should_parse_large_shallow_objects() {
592 let mut json = "{\"q\":[".to_string();
594 let size = 1_000;
595
596 for _ in 0..size {
597 json += "{\"q\":[{}]}, [\"hello\"], ";
598 }
599
600 json += "]}";
601
602 let result = parse_to_value(&json, &ParseOptions::default());
603 assert!(result.is_ok());
604 }
605}