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