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) => 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::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 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(
347 text,
348 &ParseOptions {
349 allow_comments: false,
350 allow_loose_object_property_names: false,
351 allow_trailing_commas: false,
352 allow_missing_commas: false,
353 allow_single_quoted_strings: false,
354 allow_hexadecimal_numbers: false,
355 allow_unary_plus_numbers: false,
356 },
357 );
358 match result {
359 Ok(_) => panic!("Expected error, but did not find one."),
360 Err(err) => assert_eq!(err.to_string(), message),
361 }
362 }
363
364 #[test]
365 fn it_should_error_when_has_multiple_values() {
366 assert_has_error(
367 "[][]",
368 "Text cannot contain more than one JSON value on line 1 column 3",
369 );
370 }
371
372 #[test]
373 fn it_should_error_when_object_is_not_terminated() {
374 assert_has_error("{", "Unterminated object on line 1 column 2");
375 }
376
377 #[test]
378 fn it_should_error_when_object_has_unexpected_token() {
379 assert_has_error("{ [] }", "Unexpected token in object on line 1 column 3");
380 }
381
382 #[test]
383 fn it_should_error_when_object_has_two_non_string_tokens() {
384 assert_has_error(
385 "{ asdf asdf: 5 }",
386 "Expected colon after the string or word in object property on line 1 column 8",
387 );
388 }
389
390 #[test]
391 fn it_should_error_when_array_is_not_terminated() {
392 assert_has_error("[", "Unterminated array on line 1 column 2");
393 }
394
395 #[test]
396 fn it_should_error_when_array_has_unexpected_token() {
397 assert_has_error("[:]", "Unexpected colon on line 1 column 2");
398 }
399
400 #[test]
401 fn it_should_error_when_comment_block_not_closed() {
402 assert_has_error("/* test", "Unterminated comment block on line 1 column 1");
403 }
404
405 #[test]
406 fn it_should_error_when_string_lit_not_closed() {
407 assert_has_error("\" test", "Unterminated string literal on line 1 column 1");
408 }
409
410 #[test]
411 fn strict_should_error_object_trailing_comma() {
412 assert_has_strict_error(
413 r#"{ "test": 5, }"#,
414 "Trailing commas are not allowed on line 1 column 12",
415 );
416 }
417
418 #[test]
419 fn strict_should_error_array_trailing_comma() {
420 assert_has_strict_error(r#"[ "test", ]"#, "Trailing commas are not allowed on line 1 column 9");
421 }
422
423 #[test]
424 fn strict_should_error_comment_line() {
425 assert_has_strict_error(r#"[ "test" ] // 1"#, "Comments are not allowed on line 1 column 12");
426 }
427
428 #[test]
429 fn strict_should_error_comment_block() {
430 assert_has_strict_error(r#"[ "test" /* 1 */]"#, "Comments are not allowed on line 1 column 10");
431 }
432
433 #[test]
434 fn strict_should_error_word_property() {
435 assert_has_strict_error(
436 r#"{ word: 5 }"#,
437 "Expected string for object property on line 1 column 3",
438 );
439 }
440
441 #[test]
442 fn strict_should_error_single_quoted_string() {
443 assert_has_strict_error(
444 r#"{ "key": 'value' }"#,
445 "Single-quoted strings are not allowed on line 1 column 10",
446 );
447 }
448
449 #[test]
450 fn strict_should_error_hexadecimal_number() {
451 assert_has_strict_error(
452 r#"{ "key": 0xFF }"#,
453 "Hexadecimal numbers are not allowed on line 1 column 10",
454 );
455 }
456
457 #[test]
458 fn strict_should_error_unary_plus_number() {
459 assert_has_strict_error(
460 r#"{ "key": +42 }"#,
461 "Unary plus on numbers is not allowed on line 1 column 10",
462 );
463 }
464
465 #[test]
466 fn missing_comma_between_properties() {
467 let text = r#"{
468 "name": "alice"
469 "age": 25
470}"#;
471 let value = parse_to_value(text, &Default::default()).unwrap().unwrap();
472 let obj = match &value {
473 JsonValue::Object(o) => o,
474 _ => panic!("Expected object"),
475 };
476 assert_eq!(obj.get_number("age").unwrap(), "25");
477
478 assert_has_strict_error(text, "Expected comma on line 2 column 18");
480 }
481
482 #[test]
483 fn missing_comma_with_comment_between_properties() {
484 let result = parse_to_value(
487 r#"{
488 "name": "alice" // comment here
489 "age": 25
490}"#,
491 &ParseOptions {
492 allow_comments: true,
493 allow_missing_commas: false,
494 ..Default::default()
495 },
496 );
497 match result {
498 Ok(_) => panic!("Expected error, but did not find one."),
499 Err(err) => assert_eq!(err.to_string(), "Expected comma on line 2 column 18"),
500 }
501 }
502
503 #[test]
504 fn it_should_parse_unquoted_keys_with_hex_and_trailing_comma() {
505 let text = r#"{
506 CP_CanFuncReqId: 0x7DF, // 2015
507 }"#;
508 let value = parse_to_value(text, &Default::default()).unwrap().unwrap();
509 let obj = match &value {
510 JsonValue::Object(o) => o,
511 _ => panic!("Expected object"),
512 };
513 assert_eq!(obj.get_number("CP_CanFuncReqId").unwrap(), "0x7DF");
514 }
515
516 #[test]
517 fn it_should_parse_unary_plus_numbers() {
518 let value = parse_to_value(r#"{ "test": +42 }"#, &Default::default())
519 .unwrap()
520 .unwrap();
521 let obj = match &value {
522 JsonValue::Object(o) => o,
523 _ => panic!("Expected object"),
524 };
525 assert_eq!(obj.get_number("test").unwrap(), "+42");
526 }
527
528 #[test]
529 fn it_should_parse_large_shallow_objects() {
530 let mut json = "{\"q\":[".to_string();
532 let size = 1_000;
533
534 for _ in 0..size {
535 json += "{\"q\":[{}]}, [\"hello\"], ";
536 }
537
538 json += "]}";
539
540 let result = parse_to_value(&json, &ParseOptions::default());
541 assert!(result.is_ok());
542 }
543}