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