1use crate::deserialize::error::DeserializeError as Error;
7use crate::deserialize::must_not_be_finite;
8use crate::escape::unescape_string;
9pub use crate::escape::EscapeError;
10use aws_smithy_types::date_time::Format;
11use aws_smithy_types::primitive::Parse;
12use aws_smithy_types::{base64, Blob, DateTime, Document, Number};
13use std::borrow::Cow;
14use std::collections::HashMap;
15use std::iter::Peekable;
16
17#[derive(Debug, PartialEq, Eq, Copy, Clone)]
20pub struct EscapedStr<'a>(&'a str);
21
22impl<'a> EscapedStr<'a> {
23 pub fn new(value: &'a str) -> EscapedStr<'a> {
24 EscapedStr(value)
25 }
26
27 pub fn as_escaped_str(&self) -> &'a str {
29 self.0
30 }
31
32 pub fn to_unescaped(self) -> Result<Cow<'a, str>, EscapeError> {
35 unescape_string(self.0)
36 }
37}
38
39#[derive(Debug, Eq, PartialEq, Copy, Clone)]
41pub struct Offset(pub usize);
42
43impl Offset {
44 pub fn error(&self, msg: Cow<'static, str>) -> Error {
46 Error::custom(msg).with_offset(self.0)
47 }
48}
49
50#[derive(Debug, PartialEq)]
53pub enum Token<'a> {
54 StartArray {
55 offset: Offset,
56 },
57 EndArray {
58 offset: Offset,
59 },
60 ObjectKey {
61 offset: Offset,
62 key: EscapedStr<'a>,
63 },
64 StartObject {
65 offset: Offset,
66 },
67 EndObject {
68 offset: Offset,
69 },
70 ValueBool {
71 offset: Offset,
72 value: bool,
73 },
74 ValueNull {
75 offset: Offset,
76 },
77 ValueNumber {
78 offset: Offset,
79 value: Number,
80 },
81 ValueString {
82 offset: Offset,
83 value: EscapedStr<'a>,
84 },
85}
86
87impl<'a> Token<'a> {
88 pub fn offset(&self) -> Offset {
89 use Token::*;
90 *match self {
91 StartArray { offset } => offset,
92 EndArray { offset } => offset,
93 ObjectKey { offset, .. } => offset,
94 StartObject { offset } => offset,
95 EndObject { offset } => offset,
96 ValueBool { offset, .. } => offset,
97 ValueNull { offset } => offset,
98 ValueNumber { offset, .. } => offset,
99 ValueString { offset, .. } => offset,
100 }
101 }
102
103 pub fn error(&self, msg: Cow<'static, str>) -> Error {
105 self.offset().error(msg)
106 }
107}
108
109macro_rules! expect_fn {
110 ($name:ident, $token:ident, $doc:tt) => {
111 #[doc=$doc]
112 pub fn $name(token_result: Option<Result<Token<'_>, Error>>) -> Result<(), Error> {
113 match token_result.transpose()? {
114 Some(Token::$token { .. }) => Ok(()),
115 Some(token) => {
116 Err(token.error(Cow::Borrowed(concat!("expected ", stringify!($token)))))
117 }
118 None => Err(Error::custom(concat!("expected ", stringify!($token)))),
119 }
120 }
121 };
122}
123
124expect_fn!(
125 expect_start_object,
126 StartObject,
127 "Expects a [Token::StartObject] token and returns an error if it's not present."
128);
129expect_fn!(
130 expect_start_array,
131 StartArray,
132 "Expects a [Token::StartArray] token and returns an error if it's not present."
133);
134
135macro_rules! expect_value_or_null_fn {
136 ($name:ident, $token:ident, $typ:ident, $doc:tt) => {
137 #[doc=$doc]
138 pub fn $name(token: Option<Result<Token<'_>, Error>>) -> Result<Option<$typ>, Error> {
139 match token.transpose()? {
140 Some(Token::ValueNull { .. }) => Ok(None),
141 Some(Token::$token { value, .. }) => Ok(Some(value)),
142 _ => Err(Error::custom(concat!(
143 "expected ",
144 stringify!($token),
145 " or ValueNull"
146 ))),
147 }
148 }
149 };
150}
151
152expect_value_or_null_fn!(expect_bool_or_null, ValueBool, bool, "Expects a [Token::ValueBool] or [Token::ValueNull], and returns the bool value if it's not null.");
153expect_value_or_null_fn!(expect_string_or_null, ValueString, EscapedStr, "Expects a [Token::ValueString] or [Token::ValueNull], and returns the [EscapedStr] value if it's not null.");
154
155pub fn expect_number_or_null(
160 token: Option<Result<Token<'_>, Error>>,
161) -> Result<Option<Number>, Error> {
162 match token.transpose()? {
163 Some(Token::ValueNull { .. }) => Ok(None),
164 Some(Token::ValueNumber { value, .. }) => Ok(Some(value)),
165 Some(Token::ValueString { value, offset }) => match value.to_unescaped() {
166 Err(err) => Err(Error::custom_source( "expected a valid string, escape was invalid", err).with_offset(offset.0)),
167 Ok(v) => f64::parse_smithy_primitive(v.as_ref())
168 .map_err(|_|())
170 .and_then(must_not_be_finite)
172 .map(|float| Some(aws_smithy_types::Number::Float(float)))
173 .map_err(|_| {
175 Error::custom(
176 format!(
177 "only `Infinity`, `-Infinity`, `NaN` can represent a float as a string but found `{}`",
178 v
179 )).with_offset(offset.0)
180 }),
181 },
182 _ => Err(Error::custom(
183 "expected ValueString, ValueNumber, or ValueNull",
184 )),
185 }
186}
187
188pub fn expect_blob_or_null(token: Option<Result<Token<'_>, Error>>) -> Result<Option<Blob>, Error> {
190 Ok(match expect_string_or_null(token)? {
191 Some(value) => Some(Blob::new(
192 base64::decode(value.as_escaped_str())
193 .map_err(|err| Error::custom_source("failed to decode base64", err))?,
194 )),
195 None => None,
196 })
197}
198
199pub fn expect_timestamp_or_null(
203 token: Option<Result<Token<'_>, Error>>,
204 timestamp_format: Format,
205) -> Result<Option<DateTime>, Error> {
206 Ok(match timestamp_format {
207 Format::EpochSeconds => expect_number_or_null(token)?
208 .map(|v| v.to_f64_lossy())
209 .map(|v| {
210 if v.is_nan() {
211 Err(Error::custom("NaN is not a valid epoch"))
212 } else if v.is_infinite() {
213 Err(Error::custom("infinity is not a valid epoch"))
214 } else {
215 Ok(DateTime::from_secs_f64(v))
216 }
217 })
218 .transpose()?,
219 Format::DateTime | Format::HttpDate | Format::DateTimeWithOffset => {
220 expect_string_or_null(token)?
221 .map(|v| DateTime::from_str(v.as_escaped_str(), timestamp_format))
222 .transpose()
223 .map_err(|err| Error::custom_source("failed to parse timestamp", err))?
224 }
225 })
226}
227
228pub fn expect_document<'a, I>(tokens: &mut Peekable<I>) -> Result<Document, Error>
230where
231 I: Iterator<Item = Result<Token<'a>, Error>>,
232{
233 expect_document_inner(tokens, 0)
234}
235
236const MAX_DOCUMENT_RECURSION: usize = 256;
237
238fn expect_document_inner<'a, I>(tokens: &mut Peekable<I>, depth: usize) -> Result<Document, Error>
239where
240 I: Iterator<Item = Result<Token<'a>, Error>>,
241{
242 if depth >= MAX_DOCUMENT_RECURSION {
243 return Err(Error::custom(
244 "exceeded max recursion depth while parsing document",
245 ));
246 }
247 match tokens.next().transpose()? {
248 Some(Token::ValueNull { .. }) => Ok(Document::Null),
249 Some(Token::ValueBool { value, .. }) => Ok(Document::Bool(value)),
250 Some(Token::ValueNumber { value, .. }) => Ok(Document::Number(value)),
251 Some(Token::ValueString { value, .. }) => {
252 Ok(Document::String(value.to_unescaped()?.into_owned()))
253 }
254 Some(Token::StartObject { .. }) => {
255 let mut object = HashMap::new();
256 loop {
257 match tokens.next().transpose()? {
258 Some(Token::EndObject { .. }) => break,
259 Some(Token::ObjectKey { key, .. }) => {
260 let key = key.to_unescaped()?.into_owned();
261 let value = expect_document_inner(tokens, depth + 1)?;
262 object.insert(key, value);
263 }
264 _ => return Err(Error::custom("expected object key or end object")),
265 }
266 }
267 Ok(Document::Object(object))
268 }
269 Some(Token::StartArray { .. }) => {
270 let mut array = Vec::new();
271 loop {
272 match tokens.peek() {
273 Some(Ok(Token::EndArray { .. })) => {
274 tokens.next().transpose().unwrap();
275 break;
276 }
277 _ => array.push(expect_document_inner(tokens, depth + 1)?),
278 }
279 }
280 Ok(Document::Array(array))
281 }
282 Some(Token::EndObject { .. }) | Some(Token::ObjectKey { .. }) => {
283 unreachable!("end object and object key are handled in start object")
284 }
285 Some(Token::EndArray { .. }) => unreachable!("end array is handled in start array"),
286 None => Err(Error::custom("expected value")),
287 }
288}
289
290pub fn skip_value<'a>(
292 tokens: &mut impl Iterator<Item = Result<Token<'a>, Error>>,
293) -> Result<(), Error> {
294 skip_inner(0, tokens)
295}
296
297pub fn skip_to_end<'a>(
300 tokens: &mut impl Iterator<Item = Result<Token<'a>, Error>>,
301) -> Result<(), Error> {
302 skip_inner(1, tokens)
303}
304
305fn skip_inner<'a>(
306 depth: isize,
307 tokens: &mut impl Iterator<Item = Result<Token<'a>, Error>>,
308) -> Result<(), Error> {
309 loop {
310 match tokens.next().transpose()? {
311 Some(Token::StartObject { .. }) | Some(Token::StartArray { .. }) => {
312 skip_inner(depth + 1, tokens)?;
313 if depth == 0 {
314 break;
315 }
316 }
317 Some(Token::EndObject { .. }) | Some(Token::EndArray { .. }) => {
318 debug_assert!(depth > 0);
319 break;
320 }
321 Some(Token::ValueNull { .. })
322 | Some(Token::ValueBool { .. })
323 | Some(Token::ValueNumber { .. })
324 | Some(Token::ValueString { .. }) => {
325 if depth == 0 {
326 break;
327 }
328 }
329 Some(Token::ObjectKey { .. }) => {}
330 _ => return Err(Error::custom("expected value")),
331 }
332 }
333 Ok(())
334}
335
336#[cfg(test)]
337pub mod test {
338 use super::*;
339 use crate::deserialize::error::DeserializeErrorKind as ErrorKind;
340 use crate::deserialize::error::DeserializeErrorKind::UnexpectedToken;
341 use crate::deserialize::json_token_iter;
342
343 pub fn start_array<'a>(offset: usize) -> Option<Result<Token<'a>, Error>> {
344 Some(Ok(Token::StartArray {
345 offset: Offset(offset),
346 }))
347 }
348
349 pub fn end_array<'a>(offset: usize) -> Option<Result<Token<'a>, Error>> {
350 Some(Ok(Token::EndArray {
351 offset: Offset(offset),
352 }))
353 }
354
355 pub fn start_object<'a>(offset: usize) -> Option<Result<Token<'a>, Error>> {
356 Some(Ok(Token::StartObject {
357 offset: Offset(offset),
358 }))
359 }
360
361 pub fn end_object<'a>(offset: usize) -> Option<Result<Token<'a>, Error>> {
362 Some(Ok(Token::EndObject {
363 offset: Offset(offset),
364 }))
365 }
366
367 pub fn object_key(offset: usize, key: &str) -> Option<Result<Token<'_>, Error>> {
368 Some(Ok(Token::ObjectKey {
369 offset: Offset(offset),
370 key: EscapedStr::new(key),
371 }))
372 }
373
374 pub fn value_bool<'a>(offset: usize, boolean: bool) -> Option<Result<Token<'a>, Error>> {
375 Some(Ok(Token::ValueBool {
376 offset: Offset(offset),
377 value: boolean,
378 }))
379 }
380
381 pub fn value_number<'a>(offset: usize, number: Number) -> Option<Result<Token<'a>, Error>> {
382 Some(Ok(Token::ValueNumber {
383 offset: Offset(offset),
384 value: number,
385 }))
386 }
387
388 pub fn value_null<'a>(offset: usize) -> Option<Result<Token<'a>, Error>> {
389 Some(Ok(Token::ValueNull {
390 offset: Offset(offset),
391 }))
392 }
393
394 pub fn value_string(offset: usize, string: &str) -> Option<Result<Token<'_>, Error>> {
395 Some(Ok(Token::ValueString {
396 offset: Offset(offset),
397 value: EscapedStr::new(string),
398 }))
399 }
400
401 #[track_caller]
402 fn expect_err_custom<T>(message: &str, offset: Option<usize>, result: Result<T, Error>) {
403 let err = result.err().expect("expected error");
404 let (actual_message, actual_offset) = match &err.kind {
405 ErrorKind::Custom { message, .. } => (message.as_ref(), err.offset),
406 _ => panic!("expected ErrorKind::Custom, got {:?}", err),
407 };
408 assert_eq!((message, offset), (actual_message, actual_offset));
409 }
410
411 #[test]
412 fn skip_simple_value() {
413 let mut tokens = json_token_iter(b"null true");
414 skip_value(&mut tokens).unwrap();
415 assert!(matches!(
416 tokens.next(),
417 Some(Ok(Token::ValueBool { value: true, .. }))
418 ))
419 }
420
421 #[test]
422 fn skip_array() {
423 let mut tokens = json_token_iter(b"[1, 2, 3, 4] true");
424 skip_value(&mut tokens).unwrap();
425 assert!(matches!(
426 tokens.next(),
427 Some(Ok(Token::ValueBool { value: true, .. }))
428 ))
429 }
430
431 #[test]
432 fn skip_object() {
433 let mut tokens = json_token_iter(b"{\"one\": 5, \"two\": 3} true");
434 skip_value(&mut tokens).unwrap();
435 assert!(matches!(
436 tokens.next(),
437 Some(Ok(Token::ValueBool { value: true, .. }))
438 ))
439 }
440
441 #[test]
442 fn test_skip_to_end() {
443 let tokens = json_token_iter(b"{\"one\": { \"two\": [] }, \"three\":2 }");
444 let mut tokens = tokens.skip(2);
445 assert!(matches!(tokens.next(), Some(Ok(Token::StartObject { .. }))));
446 skip_to_end(&mut tokens).unwrap();
447 match tokens.next() {
448 Some(Ok(Token::ObjectKey { key, .. })) => {
449 assert_eq!("three", key.as_escaped_str());
450 }
451 _ => panic!("expected object key three"),
452 }
453 }
454
455 #[test]
456 fn test_non_finite_floats() {
457 let mut tokens = json_token_iter(b"inf");
458 tokens
459 .next()
460 .expect("there is a token")
461 .expect_err("but it is invalid, ensure that Rust float boundary cases don't parse");
462 }
463
464 #[test]
465 fn mismatched_braces() {
466 assert!(matches!(
469 skip_value(&mut json_token_iter(br#"[{"foo": 5]}"#)),
470 Err(Error {
471 kind: UnexpectedToken(']', "'}', ','"),
472 offset: Some(10)
473 })
474 ));
475 assert!(matches!(
476 skip_value(&mut json_token_iter(br#"{"foo": 5]}"#)),
477 Err(Error {
478 kind: UnexpectedToken(']', "'}', ','"),
479 offset: Some(9)
480 })
481 ));
482 assert!(matches!(
483 skip_value(&mut json_token_iter(br#"[5,6}"#)),
484 Err(Error {
485 kind: UnexpectedToken('}', "']', ','"),
486 offset: Some(4)
487 })
488 ));
489 }
490
491 #[test]
492 fn skip_nested() {
493 let mut tokens = json_token_iter(
494 br#"
495 {"struct": {"foo": 5, "bar": 11, "arr": [1, 2, 3, {}, 5, []]},
496 "arr": [[], [[]], [{"arr":[]}]],
497 "simple": "foo"}
498 true
499 "#,
500 );
501 skip_value(&mut tokens).unwrap();
502 assert!(matches!(
503 tokens.next(),
504 Some(Ok(Token::ValueBool { value: true, .. }))
505 ))
506 }
507
508 #[test]
509 fn test_expect_start_object() {
510 expect_err_custom(
511 "expected StartObject",
512 Some(2),
513 expect_start_object(value_bool(2, true)),
514 );
515 assert!(expect_start_object(start_object(0)).is_ok());
516 }
517
518 #[test]
519 fn test_expect_start_array() {
520 expect_err_custom(
521 "expected StartArray",
522 Some(2),
523 expect_start_array(value_bool(2, true)),
524 );
525 assert!(expect_start_array(start_array(0)).is_ok());
526 }
527
528 #[test]
529 fn test_expect_string_or_null() {
530 assert_eq!(None, expect_string_or_null(value_null(0)).unwrap());
531 assert_eq!(
532 Some(EscapedStr("test\\n")),
533 expect_string_or_null(value_string(0, "test\\n")).unwrap()
534 );
535 expect_err_custom(
536 "expected ValueString or ValueNull",
537 None,
538 expect_string_or_null(value_bool(0, true)),
539 );
540 }
541
542 #[test]
543 fn test_expect_number_or_null() {
544 assert_eq!(None, expect_number_or_null(value_null(0)).unwrap());
545 assert_eq!(
546 Some(Number::PosInt(5)),
547 expect_number_or_null(value_number(0, Number::PosInt(5))).unwrap()
548 );
549 expect_err_custom(
550 "expected ValueString, ValueNumber, or ValueNull",
551 None,
552 expect_number_or_null(value_bool(0, true)),
553 );
554 assert_eq!(
555 Some(Number::Float(f64::INFINITY)),
556 expect_number_or_null(value_string(0, "Infinity")).unwrap()
557 );
558 expect_err_custom(
559 "only `Infinity`, `-Infinity`, `NaN` can represent a float as a string but found `123`",
560 Some(0),
561 expect_number_or_null(value_string(0, "123")),
562 );
563 match expect_number_or_null(value_string(0, "NaN")) {
564 Ok(Some(Number::Float(v))) if v.is_nan() => {
565 }
567 not_ok => {
568 panic!("expected nan, found: {:?}", not_ok)
569 }
570 }
571 }
572
573 #[test]
574 fn test_expect_blob_or_null() {
575 assert_eq!(None, expect_blob_or_null(value_null(0)).unwrap());
576 assert_eq!(
577 Some(Blob::new(b"hello!".to_vec())),
578 expect_blob_or_null(value_string(0, "aGVsbG8h")).unwrap()
579 );
580 expect_err_custom(
581 "expected ValueString or ValueNull",
582 None,
583 expect_blob_or_null(value_bool(0, true)),
584 );
585 }
586
587 #[test]
588 fn test_expect_timestamp_or_null() {
589 assert_eq!(
590 None,
591 expect_timestamp_or_null(value_null(0), Format::HttpDate).unwrap()
592 );
593 for (invalid, display_name) in &[
594 ("NaN", "NaN"),
595 ("Infinity", "infinity"),
596 ("-Infinity", "infinity"),
597 ] {
598 expect_err_custom(
599 format!("{display_name} is not a valid epoch").as_str(),
600 None,
601 expect_timestamp_or_null(value_string(0, invalid), Format::EpochSeconds),
602 );
603 }
604 assert_eq!(
605 Some(DateTime::from_secs_f64(2048.0)),
606 expect_timestamp_or_null(value_number(0, Number::Float(2048.0)), Format::EpochSeconds)
607 .unwrap()
608 );
609 assert_eq!(
610 Some(DateTime::from_secs_f64(1445412480.0)),
611 expect_timestamp_or_null(
612 value_string(0, "Wed, 21 Oct 2015 07:28:00 GMT"),
613 Format::HttpDate
614 )
615 .unwrap()
616 );
617 assert_eq!(
618 Some(DateTime::from_secs_f64(1445412480.0)),
619 expect_timestamp_or_null(value_string(0, "2015-10-21T07:28:00Z"), Format::DateTime)
620 .unwrap()
621 );
622 expect_err_custom(
623 "only `Infinity`, `-Infinity`, `NaN` can represent a float as a string but found `wrong`",
624 Some(0),
625 expect_timestamp_or_null(value_string(0, "wrong"), Format::EpochSeconds)
626 );
627 expect_err_custom(
628 "expected ValueString or ValueNull",
629 None,
630 expect_timestamp_or_null(value_number(0, Number::Float(0.0)), Format::DateTime),
631 );
632 }
633
634 #[test]
635 fn test_expect_document() {
636 let test = |value| expect_document(&mut json_token_iter(value).peekable()).unwrap();
637 assert_eq!(Document::Null, test(b"null"));
638 assert_eq!(Document::Bool(true), test(b"true"));
639 assert_eq!(Document::Number(Number::Float(3.2)), test(b"3.2"));
640 assert_eq!(Document::String("Foo\nBar".into()), test(b"\"Foo\\nBar\""));
641 assert_eq!(Document::Array(Vec::new()), test(b"[]"));
642 assert_eq!(Document::Object(HashMap::new()), test(b"{}"));
643 assert_eq!(
644 Document::Array(vec![
645 Document::Number(Number::PosInt(1)),
646 Document::Bool(false),
647 Document::String("s".into()),
648 Document::Array(Vec::new()),
649 Document::Object(HashMap::new()),
650 ]),
651 test(b"[1,false,\"s\",[],{}]")
652 );
653 assert_eq!(
654 Document::Object(
655 vec![
656 ("num".to_string(), Document::Number(Number::PosInt(1))),
657 ("bool".to_string(), Document::Bool(true)),
658 ("string".to_string(), Document::String("s".into())),
659 (
660 "array".to_string(),
661 Document::Array(vec![
662 Document::Object(
663 vec![("foo".to_string(), Document::Bool(false))]
664 .into_iter()
665 .collect(),
666 ),
667 Document::Object(
668 vec![("bar".to_string(), Document::Bool(true))]
669 .into_iter()
670 .collect(),
671 ),
672 ])
673 ),
674 (
675 "nested".to_string(),
676 Document::Object(
677 vec![("test".to_string(), Document::Null),]
678 .into_iter()
679 .collect()
680 )
681 ),
682 ]
683 .into_iter()
684 .collect()
685 ),
686 test(
687 br#"
688 { "num": 1,
689 "bool": true,
690 "string": "s",
691 "array":
692 [{ "foo": false },
693 { "bar": true }],
694 "nested": { "test": null } }
695 "#
696 )
697 );
698 }
699
700 #[test]
701 fn test_document_recursion_limit() {
702 let mut value = String::new();
703 value.extend(std::iter::repeat('[').take(300));
704 value.extend(std::iter::repeat(']').take(300));
705 expect_err_custom(
706 "exceeded max recursion depth while parsing document",
707 None,
708 expect_document(&mut json_token_iter(value.as_bytes()).peekable()),
709 );
710
711 value = String::new();
712 value.extend(std::iter::repeat("{\"t\":").take(300));
713 value.push('1');
714 value.extend(std::iter::repeat('}').take(300));
715 expect_err_custom(
716 "exceeded max recursion depth while parsing document",
717 None,
718 expect_document(&mut json_token_iter(value.as_bytes()).peekable()),
719 );
720 }
721}