Skip to main content

aws_smithy_json/codec/
deserializer.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! JSON deserializer implementation.
7
8use aws_smithy_schema::serde::SerdeError;
9use aws_smithy_schema::serde::ShapeDeserializer;
10use aws_smithy_schema::{Schema, ShapeType};
11use aws_smithy_types::{
12    BigDecimal, BigInteger, Blob, DateTime, DiscriminatedDocument, Document, DocumentSettings,
13    Number,
14};
15
16use crate::codec::JsonCodecSettings;
17use crate::deserialize::{json_token_iter, Token};
18
19use std::sync::Arc;
20
21/// Maximum recursion depth for deserialization. Payloads nested deeper than
22/// this will produce a [`SerdeError`] instead of risking a stack overflow.
23/// Matches the default used by `serde_json`.
24pub(crate) const MAX_DESERIALIZE_DEPTH: u32 = 128;
25
26/// JSON deserializer that implements the ShapeDeserializer trait.
27pub struct JsonDeserializer<'a> {
28    input: &'a [u8],
29    position: usize,
30    settings: Arc<JsonCodecSettings>,
31    depth: u32,
32}
33
34impl<'a> JsonDeserializer<'a> {
35    /// Creates a new JSON deserializer with the given settings.
36    pub(crate) fn new(input: &'a [u8], settings: Arc<JsonCodecSettings>) -> Self {
37        Self {
38            input,
39            position: 0,
40            settings,
41            depth: 0,
42        }
43    }
44
45    /// Returns the current byte offset relative to the input slice supplied at construction.
46    ///
47    /// Calling this method does not advance the deserializer.
48    pub fn position(&self) -> usize {
49        self.position
50    }
51
52    /// Resolves a JSON field name to a member schema.
53    fn resolve_member<'s>(
54        &self,
55        schema: &'s Schema<'s>,
56        field_name: &str,
57    ) -> Option<&'s Schema<'s>> {
58        self.settings.field_to_member(schema, field_name)
59    }
60
61    fn remaining(&self) -> &[u8] {
62        &self.input[self.position..]
63    }
64
65    fn advance_by(&mut self, n: usize) {
66        self.position = (self.position + n).min(self.input.len());
67    }
68
69    /// Parse a JSON quoted string key directly from bytes, advancing past it.
70    /// Assumes the current position is at the opening `"`.
71    /// Returns a borrowed `&str` when no escape sequences are present (common case),
72    /// avoiding a heap allocation per JSON key.
73    fn parse_key(&mut self) -> Result<std::borrow::Cow<'a, str>, SerdeError> {
74        let start = self.position + 1; // skip opening quote
75        self.position += 1;
76        let input = self.input;
77        let remaining = &input[start..];
78        let mut i = 0;
79        let mut has_escapes = false;
80        let mut found_end = false;
81        while i < remaining.len() {
82            match remaining[i] {
83                b'"' => {
84                    found_end = true;
85                    break;
86                }
87                b'\\' => {
88                    has_escapes = true;
89                    i += 2;
90                }
91                0..=31 if self.settings.enforce_strictness => {
92                    return Err(SerdeError::invalid_input("raw control character in string"));
93                }
94                _ => i += 1,
95            }
96        }
97        if !found_end {
98            return Err(SerdeError::invalid_input("unterminated string key"));
99        }
100        self.position = start + i + 1; // advance past key bytes + closing quote
101        let key_bytes = &input[start..start + i];
102        if has_escapes {
103            let raw = std::str::from_utf8(key_bytes)
104                .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
105            Ok(std::borrow::Cow::Owned(
106                crate::escape::unescape_string(raw)
107                    .map_err(|e| SerdeError::invalid_input(e.to_string()))?
108                    .into_owned(),
109            ))
110        } else {
111            Ok(std::borrow::Cow::Borrowed(
112                std::str::from_utf8(key_bytes)
113                    .map_err(|e| SerdeError::invalid_input(e.to_string()))?,
114            ))
115        }
116    }
117
118    /// Predicate: is `s` a well-formed absolute Smithy shape ID of
119    /// the form `namespace#ShapeName` (no member component, no nested
120    /// `#`, both segments non-empty)?
121    ///
122    /// Used by [`Self::resolve_shape_id`] to short-circuit absolute
123    /// names and skip namespace prepending. Returning `false` for an
124    /// input that contains `#` indicates a malformed value (e.g.
125    /// nested `#`, empty namespace) — caller falls back to leaving
126    /// the `__type` key in the resulting map so a stray
127    /// `__type: "garbage#"` doesn't poison the surrounding document
128    /// parse.
129    fn is_absolute_shape_id(s: &str) -> bool {
130        let Some((namespace, shape_name)) = s.split_once('#') else {
131            return false;
132        };
133        !namespace.is_empty()
134            && !shape_name.is_empty()
135            && !shape_name.contains('#')
136            && !shape_name.contains('$')
137            && !namespace.contains('$')
138    }
139
140    /// Predicate: is `s` a syntactically valid Smithy shape name
141    /// (the part after `#` in an absolute shape ID)?
142    ///
143    /// Smithy identifiers per the spec begin with an ASCII letter or
144    /// underscore and continue with ASCII letters, digits, or
145    /// underscores. Returning `false` causes
146    /// [`Self::resolve_shape_id`] to leave the candidate value as a
147    /// regular `Document::String` in the resulting map rather than
148    /// produce a syntactically invalid synthesized shape ID like
149    /// `<namespace>#foo bar`.
150    fn is_relative_shape_name(s: &str) -> bool {
151        let mut bytes = s.bytes();
152        let Some(first) = bytes.next() else {
153            return false;
154        };
155        if !(first.is_ascii_alphabetic() || first == b'_') {
156            return false;
157        }
158        bytes.all(|b| b.is_ascii_alphanumeric() || b == b'_')
159    }
160
161    /// Resolves a `__type` field value to a fully-qualified shape ID
162    /// string. Absolute IDs are returned as-is; relative names
163    /// (no `#`) are resolved against `default_ns` if both are set
164    /// and the name is syntactically valid. Returns `None` to signal
165    /// the lift should be skipped (caller leaves the value in the map).
166    fn resolve_shape_id(s: &str, default_ns: Option<&str>) -> Option<String> {
167        if s.contains('#') {
168            // Absolute form (or malformed) — only lift if syntactically
169            // valid. The `default_namespace` setting is intentionally
170            // ignored on this path: an explicit absolute `__type`
171            // overrides the codec's default namespace.
172            if Self::is_absolute_shape_id(s) {
173                Some(s.to_owned())
174            } else {
175                None
176            }
177        } else {
178            // Relative form — resolve against `default_namespace` if
179            // set and the name is syntactically valid. Without a
180            // configured namespace the lift is skipped (today's
181            // legacy behavior preserved).
182            match default_ns {
183                Some(ns) if Self::is_relative_shape_name(s) => Some(format!("{ns}#{s}")),
184                _ => None,
185            }
186        }
187    }
188}
189
190impl<'a> ShapeDeserializer for JsonDeserializer<'a> {
191    fn read_struct(
192        &mut self,
193        schema: &Schema<'_>,
194        consumer: &mut dyn FnMut(&Schema<'_>, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
195    ) -> Result<(), SerdeError> {
196        self.depth += 1;
197        if self.depth > self.settings.max_depth() {
198            return Err(SerdeError::custom("maximum nesting depth exceeded"));
199        }
200        // Expect opening brace
201        self.skip_whitespace();
202        if self.remaining().is_empty() {
203            if self.settings.enforce_strictness && (!self.input.is_empty() || self.depth != 1) {
204                return Err(SerdeError::invalid_input("expected object"));
205            }
206            // Treat empty input as an empty object (e.g., empty HTTP response body)
207            self.depth -= 1;
208            return Ok(());
209        }
210        if self.remaining().first() != Some(&b'{') {
211            return Err(SerdeError::type_mismatch("expected object"));
212        }
213        self.advance_by(1);
214
215        let mut first = true;
216        loop {
217            // Stop at the end of the object; otherwise the next key/value pair starts here.
218            if self.next_element(first, b'}', "object")? {
219                break;
220            }
221            first = false;
222            if self.remaining().first() != Some(&b'"') {
223                return Err(SerdeError::invalid_input("expected object key"));
224            }
225
226            // Parse the key directly from bytes
227            let key_str = self.parse_key()?;
228
229            // Skip whitespace and expect colon
230            self.skip_whitespace();
231            if self.remaining().first() != Some(&b':') {
232                return Err(SerdeError::invalid_input("expected colon after key"));
233            }
234            self.advance_by(1);
235            self.skip_whitespace();
236
237            // Document null is a value. Existing consumers skip other null members; schema
238            // server readers opt in to receive them and apply their builder semantics.
239            let member_schema = self.resolve_member(schema, &key_str);
240            let rem = self.remaining();
241            if !self.settings.dispatch_null_members()
242                && rem.starts_with(b"null")
243                && !rem.get(4).is_some_and(|b| b.is_ascii_alphanumeric())
244                && !member_schema.is_some_and(|member| member.shape_type() == ShapeType::Document)
245            {
246                self.advance_by(4);
247            } else if let Some(member_schema) = member_schema {
248                consumer(member_schema, self)?;
249            } else if &*key_str == "__type" {
250                // Protocol discriminator, never a member.
251                self.skip_json_value()?;
252            } else {
253                // Let the consumer decide how to handle an unknown member. If it
254                // leaves the value unread, validate and skip it here.
255                let start = self.position;
256                consumer(&aws_smithy_schema::prelude::DOCUMENT, self)?;
257                if self.position == start {
258                    self.skip_json_value()?;
259                }
260            }
261        }
262
263        self.leave_container()?;
264        Ok(())
265    }
266
267    fn read_list(
268        &mut self,
269        _schema: &Schema<'_>,
270        consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
271    ) -> Result<(), SerdeError> {
272        self.depth += 1;
273        if self.depth > self.settings.max_depth() {
274            return Err(SerdeError::custom("maximum nesting depth exceeded"));
275        }
276        self.skip_whitespace();
277        if self.remaining().first() != Some(&b'[') {
278            return Err(SerdeError::type_mismatch("expected array"));
279        }
280        self.advance_by(1);
281
282        let mut first = true;
283        loop {
284            if self.next_element(first, b']', "array")? {
285                break;
286            }
287            first = false;
288            consumer(self)?;
289        }
290
291        self.leave_container()?;
292        Ok(())
293    }
294
295    fn read_map(
296        &mut self,
297        _schema: &Schema<'_>,
298        consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
299    ) -> Result<(), SerdeError> {
300        self.depth += 1;
301        if self.depth > self.settings.max_depth() {
302            return Err(SerdeError::custom("maximum nesting depth exceeded"));
303        }
304        self.skip_whitespace();
305        if self.remaining().first() != Some(&b'{') {
306            return Err(SerdeError::type_mismatch("expected object"));
307        }
308        self.advance_by(1);
309
310        let mut first = true;
311        loop {
312            if self.next_element(first, b'}', "object")? {
313                break;
314            }
315            first = false;
316            if self.remaining().first() != Some(&b'"') {
317                return Err(SerdeError::invalid_input("expected key"));
318            }
319
320            let key = self.parse_key()?;
321
322            self.skip_whitespace();
323            if self.remaining().first() != Some(&b':') {
324                return Err(SerdeError::invalid_input("expected colon"));
325            }
326            self.advance_by(1);
327            self.skip_whitespace();
328
329            consumer(key.into_owned(), self)?;
330        }
331
332        self.leave_container()?;
333        Ok(())
334    }
335
336    fn read_boolean(&mut self, _schema: &Schema<'_>) -> Result<bool, SerdeError> {
337        self.skip_whitespace();
338        let rem = self.remaining();
339        if rem.starts_with(b"true") {
340            self.advance_by(4);
341            Ok(true)
342        } else if rem.starts_with(b"false") {
343            self.advance_by(5);
344            Ok(false)
345        } else {
346            Err(SerdeError::type_mismatch("expected boolean"))
347        }
348    }
349
350    fn read_byte(&mut self, _schema: &Schema<'_>) -> Result<i8, SerdeError> {
351        self.read_integer_value().and_then(|n| {
352            i8::try_from(n).map_err(|_| SerdeError::invalid_input("value out of range for byte"))
353        })
354    }
355
356    fn read_short(&mut self, _schema: &Schema<'_>) -> Result<i16, SerdeError> {
357        self.read_integer_value().and_then(|n| {
358            i16::try_from(n).map_err(|_| SerdeError::invalid_input("value out of range for short"))
359        })
360    }
361
362    fn read_integer(&mut self, _schema: &Schema<'_>) -> Result<i32, SerdeError> {
363        self.read_integer_value().and_then(|n| {
364            i32::try_from(n)
365                .map_err(|_| SerdeError::invalid_input("value out of range for integer"))
366        })
367    }
368
369    fn read_long(&mut self, _schema: &Schema<'_>) -> Result<i64, SerdeError> {
370        self.read_integer_value()
371    }
372
373    fn read_float(&mut self, _schema: &Schema<'_>) -> Result<f32, SerdeError> {
374        self.read_float_value().map(|f| f as f32)
375    }
376
377    fn read_double(&mut self, _schema: &Schema<'_>) -> Result<f64, SerdeError> {
378        self.read_float_value()
379    }
380
381    fn read_big_integer(&mut self, _schema: &Schema<'_>) -> Result<BigInteger, SerdeError> {
382        use std::str::FromStr;
383        self.skip_whitespace();
384        match self.remaining().first() {
385            Some(b'-') | Some(b'0'..=b'9') => {
386                let start = self.position;
387                self.consume_number()?;
388                let num_str = std::str::from_utf8(&self.input[start..self.position])
389                    .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
390                BigInteger::from_str(num_str).map_err(|e| SerdeError::invalid_input(e.to_string()))
391            }
392            // String-encoded big integers are produced by senders
393            // configured with `use_string_for_arbitrary_precision`. The
394            // read side accepts both wire forms unconditionally so a
395            // sender configured for one form interoperates with a
396            // receiver configured for the other.
397            Some(b'"') => {
398                let s = self.read_string(_schema)?;
399                BigInteger::from_str(&s).map_err(|e| SerdeError::invalid_input(e.to_string()))
400            }
401            _ => Err(SerdeError::type_mismatch("expected number or string")),
402        }
403    }
404
405    fn read_big_decimal(&mut self, _schema: &Schema<'_>) -> Result<BigDecimal, SerdeError> {
406        use std::str::FromStr;
407        self.skip_whitespace();
408        match self.remaining().first() {
409            Some(b'-') | Some(b'0'..=b'9') => {
410                let start = self.position;
411                self.consume_number()?;
412                let num_str = std::str::from_utf8(&self.input[start..self.position])
413                    .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
414                BigDecimal::from_str(num_str).map_err(|e| SerdeError::invalid_input(e.to_string()))
415            }
416            // String-encoded big decimals — see the symmetric comment
417            // on `read_big_integer`.
418            Some(b'"') => {
419                let s = self.read_string(_schema)?;
420                BigDecimal::from_str(&s).map_err(|e| SerdeError::invalid_input(e.to_string()))
421            }
422            _ => Err(SerdeError::type_mismatch("expected number or string")),
423        }
424    }
425
426    fn read_string(&mut self, _schema: &Schema<'_>) -> Result<String, SerdeError> {
427        self.skip_whitespace();
428        let pos = self.position;
429        let input = self.input;
430        let rem = &input[pos..];
431        if rem.first() != Some(&b'"') {
432            return Err(SerdeError::type_mismatch("expected string"));
433        }
434        // Scan for end of string, tracking whether escapes are present
435        let mut i = 1;
436        let mut has_escape = false;
437        while i < rem.len() {
438            if rem[i] == b'\\' {
439                has_escape = true;
440                i += 2;
441            } else if rem[i] == b'"' {
442                let raw = &input[pos + 1..pos + i];
443                self.position = pos + i + 1;
444                if !has_escape {
445                    return std::str::from_utf8(raw)
446                        .map(|s| s.to_owned())
447                        .map_err(|e| SerdeError::invalid_input(e.to_string()));
448                }
449                let s = std::str::from_utf8(raw)
450                    .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
451                return crate::deserialize::EscapedStr::new(s)
452                    .to_unescaped()
453                    .map(|s| s.into_owned())
454                    .map_err(|e| SerdeError::invalid_input(e.to_string()));
455            } else {
456                if self.settings.enforce_strictness && rem[i] < 32 {
457                    return Err(SerdeError::invalid_input("raw control character in string"));
458                }
459                i += 1;
460            }
461        }
462        Err(SerdeError::invalid_input("unterminated string"))
463    }
464
465    fn read_blob(&mut self, _schema: &Schema<'_>) -> Result<Blob, SerdeError> {
466        let s = self.read_string(_schema)?;
467        let decoded = aws_smithy_types::base64::decode(&s)
468            .map_err(|e| SerdeError::invalid_input(format!("invalid base64: {}", e)))?;
469        Ok(Blob::new(decoded))
470    }
471
472    fn read_string_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<String>, SerdeError> {
473        self.depth += 1;
474        if self.depth > self.settings.max_depth() {
475            return Err(SerdeError::custom("maximum nesting depth exceeded"));
476        }
477        self.skip_whitespace();
478        if self.remaining().first() != Some(&b'[') {
479            return Err(SerdeError::type_mismatch("expected array"));
480        }
481        self.advance_by(1);
482        let mut out = Vec::new();
483        let mut first = true;
484        loop {
485            if self.next_element(first, b']', "array")? {
486                break;
487            }
488            first = false;
489            out.push(self.read_string(_schema)?);
490        }
491        self.leave_container()?;
492        Ok(out)
493    }
494
495    fn read_blob_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<Blob>, SerdeError> {
496        self.depth += 1;
497        if self.depth > self.settings.max_depth() {
498            return Err(SerdeError::custom("maximum nesting depth exceeded"));
499        }
500        self.skip_whitespace();
501        if self.remaining().first() != Some(&b'[') {
502            return Err(SerdeError::type_mismatch("expected array"));
503        }
504        self.advance_by(1);
505        let mut out = Vec::new();
506        let mut first = true;
507        loop {
508            if self.next_element(first, b']', "array")? {
509                break;
510            }
511            first = false;
512            out.push(self.read_blob(_schema)?);
513        }
514        self.leave_container()?;
515        Ok(out)
516    }
517
518    fn read_integer_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<i32>, SerdeError> {
519        self.depth += 1;
520        if self.depth > self.settings.max_depth() {
521            return Err(SerdeError::custom("maximum nesting depth exceeded"));
522        }
523        self.skip_whitespace();
524        if self.remaining().first() != Some(&b'[') {
525            return Err(SerdeError::type_mismatch("expected array"));
526        }
527        self.advance_by(1);
528        let mut out = Vec::new();
529        let mut first = true;
530        loop {
531            if self.next_element(first, b']', "array")? {
532                break;
533            }
534            first = false;
535            out.push(self.read_integer(_schema)?);
536        }
537        self.leave_container()?;
538        Ok(out)
539    }
540
541    fn read_long_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<i64>, SerdeError> {
542        self.depth += 1;
543        if self.depth > self.settings.max_depth() {
544            return Err(SerdeError::custom("maximum nesting depth exceeded"));
545        }
546        self.skip_whitespace();
547        if self.remaining().first() != Some(&b'[') {
548            return Err(SerdeError::type_mismatch("expected array"));
549        }
550        self.advance_by(1);
551        let mut out = Vec::new();
552        let mut first = true;
553        loop {
554            if self.next_element(first, b']', "array")? {
555                break;
556            }
557            first = false;
558            out.push(self.read_long(_schema)?);
559        }
560        self.leave_container()?;
561        Ok(out)
562    }
563
564    fn read_string_string_map(
565        &mut self,
566        _schema: &Schema<'_>,
567    ) -> Result<std::collections::HashMap<String, String>, SerdeError> {
568        self.depth += 1;
569        if self.depth > self.settings.max_depth() {
570            return Err(SerdeError::custom("maximum nesting depth exceeded"));
571        }
572        self.skip_whitespace();
573        if self.remaining().first() != Some(&b'{') {
574            return Err(SerdeError::type_mismatch("expected object"));
575        }
576        self.advance_by(1);
577        let mut out = std::collections::HashMap::new();
578        let mut first = true;
579        loop {
580            if self.next_element(first, b'}', "object")? {
581                break;
582            }
583            first = false;
584            if self.remaining().first() != Some(&b'"') {
585                return Err(SerdeError::invalid_input("expected key"));
586            }
587            let key = self.parse_key()?;
588            self.skip_whitespace();
589            if self.remaining().first() != Some(&b':') {
590                return Err(SerdeError::invalid_input("expected colon"));
591            }
592            self.advance_by(1);
593            self.skip_whitespace();
594            let val = self.read_string(_schema)?;
595            out.insert(key.into_owned(), val);
596        }
597        self.leave_container()?;
598        Ok(out)
599    }
600
601    fn read_timestamp(&mut self, schema: &Schema<'_>) -> Result<DateTime, SerdeError> {
602        use aws_smithy_schema::traits::TimestampFormat;
603        use aws_smithy_types::date_time::Format;
604
605        self.skip_whitespace();
606        let strict = self.settings.strict_timestamp_formats();
607        // The wire form the member prescribes: its `@timestampFormat`, else the codec default.
608        let format = match schema.timestamp_format().map(|t| t.format()) {
609            Some(TimestampFormat::EpochSeconds) => Format::EpochSeconds,
610            Some(TimestampFormat::DateTime) => Format::DateTime,
611            Some(TimestampFormat::HttpDate) => Format::HttpDate,
612            Some(other) => {
613                return Err(SerdeError::unsupported(format!(
614                    "unsupported timestamp format {other:?}"
615                )))
616            }
617            None => self.settings.default_timestamp_format(),
618        };
619        match self.remaining().first() {
620            Some(b'"') => {
621                let string_format = if strict {
622                    // Strict: the string must be in exactly the prescribed format, and
623                    // `epoch-seconds` is never a string. This mirrors the token-based
624                    // parser's `expect_timestamp_or_null`.
625                    if matches!(format, Format::EpochSeconds) {
626                        return Err(SerdeError::type_mismatch(
627                            "expected a JSON number for an epoch-seconds timestamp",
628                        ));
629                    }
630                    format
631                } else {
632                    // Lenient (unchanged behavior): an explicit `@timestampFormat` of
633                    // `http-date` or `epoch-seconds` is honored as written, `date-time`
634                    // parses as offset-aware `date-time`, and a member without the trait
635                    // follows the codec default via `string_timestamp_format`.
636                    match schema.timestamp_format().map(|t| t.format()) {
637                        Some(TimestampFormat::HttpDate) => Format::HttpDate,
638                        Some(TimestampFormat::EpochSeconds) => Format::EpochSeconds,
639                        Some(TimestampFormat::DateTime) => Format::DateTimeWithOffset,
640                        Some(other) => {
641                            return Err(SerdeError::unsupported(format!(
642                                "unsupported timestamp format {other:?}"
643                            )))
644                        }
645                        None => crate::codec::string_timestamp_format(
646                            self.settings.default_timestamp_format(),
647                        ),
648                    }
649                };
650                let s = self.read_string(schema)?;
651                DateTime::from_str(&s, string_format)
652                    .map_err(|e| SerdeError::custom(format!("invalid timestamp string: {e}")))
653            }
654            Some(b'-') | Some(b'0'..=b'9') => {
655                if strict && !matches!(format, Format::EpochSeconds) {
656                    return Err(SerdeError::type_mismatch(
657                        "expected a JSON string for a date-time or http-date timestamp",
658                    ));
659                }
660                // Numeric timestamp: epoch seconds.
661                let start = self.position;
662                self.consume_number()?;
663                let num_str = std::str::from_utf8(&self.input[start..self.position])
664                    .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
665                if num_str.contains('.') || num_str.contains('e') || num_str.contains('E') {
666                    let f: f64 = num_str.parse().map_err(|e: std::num::ParseFloatError| {
667                        SerdeError::invalid_input(e.to_string())
668                    })?;
669                    // The i64 range as f64. `i64::MAX as f64` rounds up to 2^63, which is
670                    // why the range is half-open: 2^63 itself overflows `floor() as i64`
671                    // and would saturate silently in `DateTime::from_secs_f64`.
672                    const I64_MIN_F64: f64 = i64::MIN as f64;
673                    const I64_MAX_F64: f64 = i64::MAX as f64;
674                    if self.settings.enforce_strictness
675                        && (!f.is_finite() || !(I64_MIN_F64..I64_MAX_F64).contains(&f))
676                    {
677                        return Err(SerdeError::invalid_input(
678                            "epoch-seconds value out of range",
679                        ));
680                    }
681                    Ok(DateTime::from_secs_f64(f))
682                } else if num_str.starts_with('-') {
683                    let n: i64 = num_str.parse().map_err(|e: std::num::ParseIntError| {
684                        SerdeError::invalid_input(e.to_string())
685                    })?;
686                    Ok(DateTime::from_secs(n))
687                } else {
688                    let n: u64 = num_str.parse().map_err(|e: std::num::ParseIntError| {
689                        SerdeError::invalid_input(e.to_string())
690                    })?;
691                    if n > i64::MAX as u64 {
692                        return Err(SerdeError::invalid_input(format!(
693                            "epoch-seconds value {n} overflows i64; cannot construct DateTime"
694                        )));
695                    }
696                    Ok(DateTime::from_secs(n as i64))
697                }
698            }
699            _ => Err(SerdeError::type_mismatch("expected timestamp")),
700        }
701    }
702
703    fn read_document(&mut self, _schema: &Schema<'_>) -> Result<Document, SerdeError> {
704        self.depth += 1;
705        if self.depth > self.settings.max_depth() {
706            return Err(SerdeError::custom("maximum nesting depth exceeded"));
707        }
708        self.skip_whitespace();
709        let result: Result<Document, SerdeError> = match self.remaining().first() {
710            Some(b'"') => Ok(Document::String(self.read_string(_schema)?)),
711            Some(b't') | Some(b'f') => Ok(Document::Bool(self.read_boolean(_schema)?)),
712            Some(b'n') => {
713                if self.remaining().starts_with(b"null") {
714                    self.advance_by(4);
715                    Ok(Document::Null)
716                } else {
717                    Err(SerdeError::invalid_input("unexpected token in document"))
718                }
719            }
720            Some(b'{') => {
721                self.advance_by(1);
722                let mut map = std::collections::HashMap::new();
723                let mut first = true;
724                loop {
725                    if self.next_element(first, b'}', "document object")? {
726                        break;
727                    }
728                    first = false;
729                    if self.remaining().first() != Some(&b'"') {
730                        return Err(SerdeError::invalid_input("expected object key in document"));
731                    }
732                    let key = self.parse_key()?.into_owned();
733                    self.skip_whitespace();
734                    if self.remaining().first() != Some(&b':') {
735                        return Err(SerdeError::invalid_input(
736                            "expected colon in document object",
737                        ));
738                    }
739                    self.advance_by(1);
740                    self.skip_whitespace();
741                    let value = self.read_document(_schema)?;
742                    map.insert(key, value);
743                }
744                Ok(Document::Object(map))
745            }
746            Some(b'[') => {
747                self.advance_by(1);
748                let mut arr = Vec::new();
749                let mut first = true;
750                loop {
751                    if self.next_element(first, b']', "document array")? {
752                        break;
753                    }
754                    first = false;
755                    arr.push(self.read_document(_schema)?);
756                }
757                Ok(Document::Array(arr))
758            }
759            Some(c) if *c == b'-' || c.is_ascii_digit() => {
760                // Parse a JSON number into [`Document::Number`].
761                // Range determines which `Number` variant carries it
762                // (PosInt / NegInt / Float).
763                //
764                // The token's extent comes from the codec's number lexer
765                // (strict JSON grammar when `enforce_strictness` is set), so
766                // an exponent sign such as `1e-5` stays part of the number.
767                let pos = self.position;
768                self.consume_number()?;
769                let token = &self.input[pos..self.position];
770                let is_negative = token.first() == Some(&b'-');
771                let is_float = token.iter().any(|b| matches!(b, b'.' | b'e' | b'E'));
772                let s = std::str::from_utf8(token)
773                    .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
774                // `Document` has exactly the six JSON-shaped variants, so
775                // every JSON number lands in `Number`. The variant is
776                // chosen by range: `PosInt(u64)` / `NegInt(i64)` for
777                // integral text that fits, `Float(f64)` otherwise.
778                //
779                // A value that overflows the integer variants is carried
780                // as `Float`, matching the released
781                // `aws_smithy_json::deserialize` token reader. This is
782                // lossy for integers past 2^53, and deliberately so: the
783                // alternative — lifting the numeric text into
784                // `Document::String` — would change a JSON number into a
785                // JSON string on re-serialization. Arbitrary-precision
786                // *shapes* do not take this path; a `bigInteger` /
787                // `bigDecimal` member has a schema, so it is read by
788                // `read_big_integer` / `read_big_decimal`, which keep the
789                // source text intact.
790                if is_float {
791                    match s.parse::<f64>() {
792                        Ok(f) => Ok(Document::Number(Number::Float(f))),
793                        Err(e) => Err(SerdeError::invalid_input(e.to_string())),
794                    }
795                } else if is_negative {
796                    match s.parse::<i64>() {
797                        Ok(n) => Ok(Document::Number(Number::NegInt(n))),
798                        // Overflowed `i64` — fall back to `f64`.
799                        Err(_) => match s.parse::<f64>() {
800                            Ok(f) => Ok(Document::Number(Number::Float(f))),
801                            Err(e) => Err(SerdeError::invalid_input(e.to_string())),
802                        },
803                    }
804                } else {
805                    match s.parse::<u64>() {
806                        Ok(n) => Ok(Document::Number(Number::PosInt(n))),
807                        // Overflowed `u64` — fall back to `f64`.
808                        Err(_) => match s.parse::<f64>() {
809                            Ok(f) => Ok(Document::Number(Number::Float(f))),
810                            Err(e) => Err(SerdeError::invalid_input(e.to_string())),
811                        },
812                    }
813                }
814            }
815            _ => Err(SerdeError::invalid_input("unexpected token in document")),
816        };
817        if result.is_ok() {
818            self.leave_container()?;
819        }
820        result
821    }
822
823    fn is_null(&self) -> bool {
824        let remaining = self.remaining();
825        remaining.len() >= 4
826            && &remaining[..4] == b"null"
827            && !remaining.get(4).is_some_and(|b| b.is_ascii_alphanumeric())
828    }
829
830    fn read_null(&mut self) -> Result<(), SerdeError> {
831        self.skip_whitespace();
832        if self.is_null() {
833            self.advance_by(4);
834        }
835        Ok(())
836    }
837
838    /// JSON shares one cursor across the whole object, so declining a value still has to
839    /// move past it. Delegates to the same validating skip used for unknown members: a
840    /// body copy of a transport-bound member is discarded, but it must still be
841    /// well-formed JSON, so malformed content is reported rather than stepped over.
842    fn skip_value(&mut self) -> Result<(), SerdeError> {
843        self.skip_json_value()
844    }
845
846    fn container_size(&self) -> Option<usize> {
847        let mut iter = json_token_iter(self.remaining());
848        match iter.next()? {
849            Ok(Token::StartArray { .. }) => {
850                let mut count = 0;
851                let mut depth = 1;
852                for token in iter {
853                    match token {
854                        Ok(Token::StartArray { .. }) | Ok(Token::StartObject { .. }) => {
855                            if depth == 1 {
856                                count += 1;
857                            }
858                            depth += 1;
859                        }
860                        Ok(Token::EndArray { .. }) | Ok(Token::EndObject { .. }) => {
861                            depth -= 1;
862                            if depth == 0 {
863                                return Some(count);
864                            }
865                        }
866                        Ok(Token::ValueBool { .. })
867                        | Ok(Token::ValueNull { .. })
868                        | Ok(Token::ValueString { .. })
869                        | Ok(Token::ValueNumber { .. })
870                            if depth == 1 =>
871                        {
872                            count += 1
873                        }
874                        _ => {}
875                    }
876                }
877                None
878            }
879            Ok(Token::StartObject { .. }) => {
880                let mut count = 0;
881                let mut depth = 1;
882                for token in iter {
883                    match token {
884                        Ok(Token::StartArray { .. }) | Ok(Token::StartObject { .. }) => depth += 1,
885                        Ok(Token::EndArray { .. }) | Ok(Token::EndObject { .. }) => {
886                            depth -= 1;
887                            if depth == 0 {
888                                return Some(count);
889                            }
890                        }
891                        Ok(Token::ObjectKey { .. }) if depth == 1 => count += 1,
892                        _ => {}
893                    }
894                }
895                None
896            }
897            _ => None,
898        }
899    }
900}
901
902impl<'a> JsonDeserializer<'a> {
903    /// Reads a JSON document from the input, lifts an optional
904    /// top-level `__type` field into the result's discriminator slot,
905    /// and attaches the codec's [`DocumentSettings`] to the result so
906    /// downstream accessors like
907    /// [`DiscriminatedDocument::as_blob`](aws_smithy_types::DiscriminatedDocument::as_blob)
908    /// and
909    /// [`DiscriminatedDocument::as_timestamp`](aws_smithy_types::DiscriminatedDocument::as_timestamp)
910    /// can perform JSON-specific coercion (base64-decoded blobs,
911    /// format-aware timestamps).
912    ///
913    /// # `__type` discriminator lift
914    ///
915    /// When the parsed top-level value is a JSON object containing a
916    /// `__type` key whose value is an absolute Smithy shape ID
917    /// (`namespace#ShapeName`), the lift extracts that ID into the
918    /// resulting [`DiscriminatedDocument`]'s discriminator slot and
919    /// drops the key from the result map.
920    ///
921    /// Falls back to leaving `__type` in the map when the value is
922    /// not a string, or is a relative shape name (no `#`) and no
923    /// `default_namespace` is configured. When a `default_namespace`
924    /// is set, a relative name is resolved against it before lifting.
925    ///
926    /// # Example
927    ///
928    /// ```
929    /// use aws_smithy_json::codec::JsonCodec;
930    /// use aws_smithy_schema::codec::Codec;
931    ///
932    /// let codec = JsonCodec::default();
933    /// let bytes = br#"{"__type":"smithy.example#Bird","name":"Iago"}"#;
934    /// let mut deser = codec.create_deserializer(bytes);
935    /// let doc = deser.read_discriminated_document().unwrap();
936    ///
937    /// // `__type` was lifted into the discriminator slot.
938    /// assert_eq!(doc.discriminator(), Some("smithy.example#Bird"));
939    ///
940    /// // The map no longer contains `__type`.
941    /// let map = doc.document().as_object().unwrap();
942    /// assert_eq!(map.len(), 1);
943    /// assert_eq!(map.get("name").and_then(|d| d.as_string()), Some("Iago"));
944    /// ```
945    pub fn read_discriminated_document(&mut self) -> Result<DiscriminatedDocument, SerdeError> {
946        // Use the trait method's walker and post-hoc lift the
947        // top-level `__type` field. The lift cost is one HashMap
948        // probe + one `String` clone — straightforward because
949        // [`Document`] is fully owned and [`DiscriminatedDocument`]
950        // carries an `Option<String>` discriminator.
951        let dummy_schema = &aws_smithy_schema::prelude::DOCUMENT;
952        let mut doc = self.read_document(dummy_schema)?;
953        let mut discriminator: Option<String> = None;
954        if let Document::Object(ref mut map) = doc {
955            // Pull the candidate value first (borrowing), validate it,
956            // then remove from the map only when the lift succeeds.
957            // This keeps a malformed `__type` value in the map so the
958            // caller can post-process if it wants to.
959            let lift_candidate = match map.get("__type") {
960                Some(Document::String(s)) => {
961                    Self::resolve_shape_id(s, self.settings.default_namespace())
962                }
963                _ => None,
964            };
965            if let Some(id) = lift_candidate {
966                map.remove("__type");
967                discriminator = Some(id);
968            }
969        }
970
971        let settings: Arc<dyn DocumentSettings> = self.settings.clone();
972        let mut wrapper = DiscriminatedDocument::new(doc).with_settings(settings);
973        if let Some(id) = discriminator {
974            wrapper = wrapper.with_discriminator(id);
975        }
976        Ok(wrapper)
977    }
978
979    fn skip_whitespace(&mut self) {
980        while self.position < self.input.len() {
981            match self.input[self.position] {
982                b' ' | b'\t' | b'\n' | b'\r' => self.position += 1,
983                _ => break,
984            }
985        }
986    }
987
988    /// Positions the deserializer at the next element of a container whose closing
989    /// delimiter is `close`, enforcing JSON's separator rules: elements are separated by
990    /// exactly one comma, and no comma may precede the closing delimiter.
991    ///
992    /// Returns `true` when the closing delimiter was reached and consumed, `false` when the
993    /// next element starts at the current position. `first` is whether no element has been
994    /// read from this container yet.
995    fn next_element(&mut self, first: bool, close: u8, what: &str) -> Result<bool, SerdeError> {
996        self.skip_whitespace();
997        match self.remaining().first().copied() {
998            Some(b) if b == close => {
999                self.advance_by(1);
1000                Ok(true)
1001            }
1002            None => Err(SerdeError::invalid_input(format!(
1003                "unexpected end of input in {what}"
1004            ))),
1005            Some(b',') if first => Err(SerdeError::invalid_input(format!(
1006                "unexpected `,` before the first element of {what}"
1007            ))),
1008            Some(b',') => {
1009                self.advance_by(1);
1010                self.skip_whitespace();
1011                match self.remaining().first().copied() {
1012                    Some(b) if b == close => {
1013                        Err(SerdeError::invalid_input(format!("trailing `,` in {what}")))
1014                    }
1015                    Some(b',') => Err(SerdeError::invalid_input(format!("repeated `,` in {what}"))),
1016                    None => Err(SerdeError::invalid_input(format!(
1017                        "unexpected end of input in {what}"
1018                    ))),
1019                    Some(_) => Ok(false),
1020                }
1021            }
1022            Some(_) if first => Ok(false),
1023            Some(_) => Err(SerdeError::invalid_input(format!(
1024                "expected `,` between elements of {what}"
1025            ))),
1026        }
1027    }
1028
1029    fn enter_container(&mut self) -> Result<(), SerdeError> {
1030        self.depth += 1;
1031        if self.depth > self.settings.max_depth() {
1032            return Err(SerdeError::custom("maximum nesting depth exceeded"));
1033        }
1034        Ok(())
1035    }
1036
1037    /// Leaves a container. When it was the outermost value of the input, nothing but
1038    /// whitespace may follow it: a body such as `{"a": 1}abc` is malformed JSON and is
1039    /// rejected here, matching the token-based parser's "found more JSON tokens after
1040    /// completing parsing" check.
1041    fn leave_container(&mut self) -> Result<(), SerdeError> {
1042        self.depth -= 1;
1043        if self.depth == 0 {
1044            self.skip_whitespace();
1045            if !self.remaining().is_empty() {
1046                return Err(SerdeError::invalid_input(
1047                    "unexpected trailing characters after the JSON value",
1048                ));
1049            }
1050        }
1051        Ok(())
1052    }
1053
1054    /// Skips a JSON number, validating it against the RFC 8259 grammar
1055    /// `-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?`. Whatever follows is left for the
1056    /// enclosing container's separator check. The configured leading-zero and
1057    /// empty-fraction and empty-integer-part allowances apply to both read and
1058    /// discarded values.
1059    fn skip_number(&mut self) -> Result<(), SerdeError> {
1060        let rem = self.remaining();
1061        let invalid = || SerdeError::invalid_input("invalid number");
1062        let mut i = 0;
1063        if rem.get(i) == Some(&b'-') {
1064            i += 1;
1065        }
1066        match rem.get(i) {
1067            Some(b'0') if !self.settings.allow_leading_zeros => i += 1,
1068            Some(b'0') => {
1069                while rem.get(i).is_some_and(|b| b.is_ascii_digit()) {
1070                    i += 1;
1071                }
1072            }
1073            Some(b'1'..=b'9') => {
1074                while rem.get(i).is_some_and(|b| b.is_ascii_digit()) {
1075                    i += 1;
1076                }
1077            }
1078            Some(b'.')
1079                if self.settings.allow_empty_integer_part
1080                    && i == 1
1081                    && rem.first() == Some(&b'-')
1082                    && rem.get(i + 1).is_some_and(|b| b.is_ascii_digit()) => {}
1083            _ => return Err(invalid()),
1084        }
1085        if rem.get(i) == Some(&b'.') {
1086            i += 1;
1087            let start = i;
1088            while rem.get(i).is_some_and(|b| b.is_ascii_digit()) {
1089                i += 1;
1090            }
1091            if i == start && !self.settings.allow_trailing_decimal_point {
1092                return Err(invalid());
1093            }
1094        }
1095        if matches!(rem.get(i), Some(b'e') | Some(b'E')) {
1096            i += 1;
1097            if matches!(rem.get(i), Some(b'+') | Some(b'-')) {
1098                i += 1;
1099            }
1100            let start = i;
1101            while rem.get(i).is_some_and(|b| b.is_ascii_digit()) {
1102                i += 1;
1103            }
1104            if i == start {
1105                return Err(invalid());
1106            }
1107        }
1108        self.advance_by(i);
1109        Ok(())
1110    }
1111
1112    /// Skips a string according to the configured discarded-value validation.
1113    fn skip_string(&mut self) -> Result<(), SerdeError> {
1114        let rem = self.remaining();
1115        debug_assert_eq!(rem.first(), Some(&b'"'));
1116        let mut i = 1;
1117        loop {
1118            match rem.get(i) {
1119                None => return Err(SerdeError::invalid_input("unterminated string")),
1120                Some(b'"') => {
1121                    i += 1;
1122                    break;
1123                }
1124                Some(b'\\') if !self.settings.validate_skipped_values => i += 2,
1125                Some(b'\\') => match rem.get(i + 1) {
1126                    Some(b'"' | b'\\' | b'/' | b'b' | b'f' | b'n' | b'r' | b't') => i += 2,
1127                    Some(b'u')
1128                        if rem
1129                            .get(i + 2..i + 6)
1130                            .is_some_and(|hex| hex.iter().all(u8::is_ascii_hexdigit)) =>
1131                    {
1132                        i += 6
1133                    }
1134                    _ => {
1135                        return Err(SerdeError::invalid_input(
1136                            "invalid escape sequence in string",
1137                        ))
1138                    }
1139                },
1140                Some(byte) if self.settings.validate_skipped_string_encoding && *byte < 0x20 => {
1141                    return Err(SerdeError::invalid_input(
1142                        "raw control character in skipped string",
1143                    ));
1144                }
1145                Some(_) => i += 1,
1146            }
1147        }
1148        if self.settings.validate_skipped_string_encoding {
1149            std::str::from_utf8(&rem[..i])
1150                .map_err(|_| SerdeError::invalid_input("invalid UTF-8 in skipped string"))?;
1151        }
1152        self.advance_by(i);
1153        Ok(())
1154    }
1155
1156    fn skip_literal(&mut self, literal: &'static [u8]) -> Result<(), SerdeError> {
1157        if !self.remaining().starts_with(literal) {
1158            return Err(SerdeError::invalid_input(format!(
1159                "expected `{}`",
1160                String::from_utf8_lossy(literal)
1161            )));
1162        }
1163        self.advance_by(literal.len());
1164        Ok(())
1165    }
1166
1167    fn consume_number(&mut self) -> Result<(), SerdeError> {
1168        if self.settings.enforce_strictness {
1169            self.skip_number()?;
1170            // Allow whitespace, JSON delimiters, or end of input after the number.
1171            if self
1172                .remaining()
1173                .first()
1174                .is_some_and(|b| !matches!(b, b' ' | b'\t' | b'\r' | b'\n' | b',' | b']' | b'}'))
1175            {
1176                return Err(SerdeError::invalid_input("invalid number boundary"));
1177            }
1178            return Ok(());
1179        }
1180        let mut len = 0;
1181        for &b in self.remaining() {
1182            if b.is_ascii_digit() || b == b'-' || b == b'.' || b == b'e' || b == b'E' || b == b'+' {
1183                len += 1;
1184            } else {
1185                break;
1186            }
1187        }
1188        self.advance_by(len);
1189        Ok(())
1190    }
1191
1192    /// Skips one JSON value, validating its syntax as it goes so that an unknown member
1193    /// cannot disrupt the enclosing container. Discarded strings (including nested
1194    /// object keys) follow `validate_skipped_values`.
1195    fn skip_json_value(&mut self) -> Result<(), SerdeError> {
1196        self.skip_whitespace();
1197        match self.remaining().first().copied() {
1198            Some(b'{') => {
1199                self.enter_container()?;
1200                self.advance_by(1);
1201                let mut first = true;
1202                loop {
1203                    if self.next_element(first, b'}', "object")? {
1204                        break;
1205                    }
1206                    first = false;
1207                    if self.remaining().first() != Some(&b'"') {
1208                        return Err(SerdeError::invalid_input("expected object key"));
1209                    }
1210                    self.skip_string()?;
1211                    self.skip_whitespace();
1212                    if self.remaining().first() != Some(&b':') {
1213                        return Err(SerdeError::invalid_input("expected colon after key"));
1214                    }
1215                    self.advance_by(1);
1216                    self.skip_json_value()?;
1217                }
1218                self.depth -= 1;
1219                Ok(())
1220            }
1221            Some(b'[') => {
1222                self.enter_container()?;
1223                self.advance_by(1);
1224                let mut first = true;
1225                loop {
1226                    if self.next_element(first, b']', "array")? {
1227                        break;
1228                    }
1229                    first = false;
1230                    self.skip_json_value()?;
1231                }
1232                self.depth -= 1;
1233                Ok(())
1234            }
1235            Some(b'"') => self.skip_string(),
1236            Some(b't') => self.skip_literal(b"true"),
1237            Some(b'f') => self.skip_literal(b"false"),
1238            Some(b'n') => self.skip_literal(b"null"),
1239            Some(c) if c == b'-' || c.is_ascii_digit() => self.skip_number(),
1240            Some(_) => Err(SerdeError::invalid_input("unexpected token in skip_value")),
1241            None => Err(SerdeError::invalid_input("unexpected end of input")),
1242        }
1243    }
1244
1245    fn read_integer_value(&mut self) -> Result<i64, SerdeError> {
1246        self.skip_whitespace();
1247        if self.settings.enforce_strictness || self.settings.allow_integral_float_numbers {
1248            let start = self.position;
1249            self.skip_number()?;
1250            if self
1251                .remaining()
1252                .first()
1253                .is_some_and(|b| !matches!(b, b' ' | b'\t' | b'\r' | b'\n' | b',' | b']' | b'}'))
1254            {
1255                return Err(SerdeError::invalid_input("invalid number boundary"));
1256            }
1257            let text = std::str::from_utf8(&self.input[start..self.position])
1258                .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
1259            if self.settings.allow_integral_float_numbers && text.contains(['.', 'e', 'E']) {
1260                return parse_integral_decimal(text);
1261            }
1262            return text
1263                .parse()
1264                .map_err(|e: std::num::ParseIntError| SerdeError::invalid_input(e.to_string()));
1265        }
1266        let rem = self.remaining();
1267        let mut len = 0;
1268        for &b in rem {
1269            if b.is_ascii_digit() || b == b'-' || b == b'+' {
1270                len += 1;
1271            } else {
1272                break;
1273            }
1274        }
1275        if len == 0 {
1276            return Err(SerdeError::type_mismatch("expected integer"));
1277        }
1278        // Reject a floating-point value for an integer member. Without this,
1279        // `1.5` would read `1` and leave `.5` in the stream, surfacing as a
1280        // confusing downstream parse error instead of a clean TypeMismatch.
1281        if let Some(&next) = rem.get(len) {
1282            if next == b'.' || next == b'e' || next == b'E' {
1283                return Err(SerdeError::type_mismatch(
1284                    "expected integer, found floating-point number",
1285                ));
1286            }
1287        }
1288        let s = std::str::from_utf8(&rem[..len])
1289            .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
1290        let n = s
1291            .parse::<i64>()
1292            .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
1293        self.advance_by(len);
1294        Ok(n)
1295    }
1296
1297    fn read_float_value(&mut self) -> Result<f64, SerdeError> {
1298        self.skip_whitespace();
1299        let rem = self.remaining();
1300        // A string may only carry a non-finite value, which JSON cannot express as a number:
1301        // `"NaN"`, `"Infinity"`, `"-Infinity"`. A quoted finite number such as `"123"` is a
1302        // type mismatch; the restJson1 protocol tests
1303        // `RestJsonBodyFloatMalformedValueRejected_case0` and
1304        // `RestJsonBodyDoubleMalformedValueRejected_case0` require it to be rejected. This
1305        // mirrors the token-based parser (`expect_number_or_null`), which parses the string
1306        // with `aws_smithy_types::primitive::Parse` and then rejects finite values, so the
1307        // spellings Rust's `f64::from_str` accepts for non-finite values (e.g. `"nan"`) are
1308        // accepted here too.
1309        if rem.first() == Some(&b'"') {
1310            let s = self.read_string(&aws_smithy_schema::prelude::STRING)?;
1311            let value = match s.as_str() {
1312                "NaN" => f64::NAN,
1313                "Infinity" => f64::INFINITY,
1314                "-Infinity" => f64::NEG_INFINITY,
1315                other => other
1316                    .parse::<f64>()
1317                    .map_err(|e| SerdeError::invalid_input(e.to_string()))?,
1318            };
1319            if value.is_finite() {
1320                return Err(SerdeError::type_mismatch(format!(
1321                    "only `Infinity`, `-Infinity`, `NaN` can represent a float as a string but found `{s}`"
1322                )));
1323            }
1324            return Ok(value);
1325        }
1326        if self.settings.enforce_strictness {
1327            let start = self.position;
1328            self.consume_number()?;
1329            return std::str::from_utf8(&self.input[start..self.position])
1330                .map_err(|e| SerdeError::invalid_input(e.to_string()))?
1331                .parse()
1332                .map_err(|e: std::num::ParseFloatError| SerdeError::invalid_input(e.to_string()));
1333        }
1334        let mut len = 0;
1335        for &b in rem {
1336            if b.is_ascii_digit() || b == b'-' || b == b'+' || b == b'.' || b == b'e' || b == b'E' {
1337                len += 1;
1338            } else {
1339                break;
1340            }
1341        }
1342        if len == 0 {
1343            return Err(SerdeError::type_mismatch("expected number"));
1344        }
1345        let s = std::str::from_utf8(&rem[..len])
1346            .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
1347        let n = s
1348            .parse::<f64>()
1349            .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
1350        self.advance_by(len);
1351        Ok(n)
1352    }
1353}
1354
1355/// Converts a JSON number written with a fraction or exponent (`1.0`, `1e3`,
1356/// `1.50E1`) to an `i64`, exactly, or fails if it is not a whole number in range.
1357///
1358/// The number is treated as `digits × 10^shift`, where `digits` is the mantissa
1359/// with the `.` removed and `shift = exponent − (digits after the '.')`. For
1360/// `1.50E1`: digits `150`, shift `1 − 2 = −1`, value `150 × 10^-1 = 15`. The
1361/// integer keeps the first `kept = significant digits + shift` of them; any it
1362/// drops must be zeros, and it can have at most the 19 digits an `i64` holds.
1363///
1364/// The arithmetic never goes through `f64`: an `f64` has a 53-bit significand,
1365/// so not every integer above 2^53 is representable. `9007199254740993.0`
1366/// (2^53 + 1) parses to the nearest `f64`, which is 2^53, and would come out
1367/// as `9007199254740992` with no error. Accumulating into an `i128` instead
1368/// cannot overflow for 19 digits, and `i64::try_from` does the range check.
1369///
1370/// `text` has passed `skip_number`, including any configured grammar allowances.
1371/// An exponent that does not fit an `i64` is rejected even when the digits are
1372/// all zero.
1373fn parse_integral_decimal(text: &str) -> Result<i64, SerdeError> {
1374    let invalid = || SerdeError::invalid_input("number is fractional or outside integer range");
1375    let negative = text.starts_with('-');
1376    let unsigned = text.strip_prefix('-').unwrap_or(text);
1377    let (mantissa, exponent) = unsigned.split_once(['e', 'E']).unwrap_or((unsigned, "0"));
1378    let (int_part, frac_part) = mantissa.split_once('.').unwrap_or((mantissa, ""));
1379    let exponent: i64 = exponent.parse().map_err(|_| invalid())?;
1380
1381    let all_digits = int_part.bytes().chain(frac_part.bytes());
1382    let leading_zeros = all_digits.clone().take_while(|b| *b == b'0').count();
1383    let digits = all_digits.skip(leading_zeros).map(|b| (b - b'0') as i128);
1384    let significant = (int_part.len() + frac_part.len() - leading_zeros) as i64;
1385    if significant == 0 {
1386        return Ok(0);
1387    }
1388    let shift = exponent
1389        .checked_sub(frac_part.len() as i64)
1390        .ok_or_else(invalid)?;
1391
1392    // Fewer than 1 kept digit means the value is a fraction; more than 19
1393    // cannot fit an i64.
1394    let kept = significant.checked_add(shift).ok_or_else(invalid)?;
1395    if !(1..=19).contains(&kept) {
1396        return Err(invalid());
1397    }
1398    let take = kept.min(significant) as usize;
1399    let mut acc: i128 = 0;
1400    for (i, d) in digits.enumerate() {
1401        if i < take {
1402            acc = acc * 10 + d; // at most 19 digits: cannot overflow i128
1403        } else if d != 0 {
1404            return Err(invalid()); // a dropped digit was not zero: fractional
1405        }
1406    }
1407    if shift > 0 {
1408        acc *= 10_i128.pow(shift as u32); // kept <= 19 keeps this below 10^19
1409    }
1410    i64::try_from(if negative { -acc } else { acc }).map_err(|_| invalid())
1411}
1412
1413#[cfg(test)]
1414mod tests {
1415    use super::*;
1416
1417    #[test]
1418    fn null_dispatch_can_clear_an_optional_duplicate_without_changing_the_default() {
1419        use aws_smithy_schema::shape_id;
1420
1421        let text = Schema::new_member(
1422            shape_id!("test", "Input", "text"),
1423            ShapeType::String,
1424            "text",
1425            0,
1426        );
1427        let members = [&text];
1428        let schema = Schema::new_struct(shape_id!("test", "Input"), ShapeType::Structure, &members);
1429        for (dispatch, expected) in [(false, Some("first".to_owned())), (true, None)] {
1430            let settings = JsonCodecSettings::builder()
1431                .dispatch_null_members(dispatch)
1432                .build();
1433            let mut deserializer =
1434                JsonDeserializer::new(br#"{"text":"first","text":null}"#, Arc::new(settings));
1435            let mut value = None;
1436            deserializer
1437                .read_struct(&schema, &mut |member, reader| {
1438                    if reader.is_null() {
1439                        reader.read_null()?;
1440                        value = None;
1441                    } else {
1442                        value = Some(reader.read_string(member)?);
1443                    }
1444                    Ok(())
1445                })
1446                .unwrap();
1447            assert_eq!(value, expected);
1448        }
1449    }
1450
1451    #[test]
1452    fn document_member_null_is_dispatched_as_a_value() {
1453        use aws_smithy_schema::shape_id;
1454
1455        let document = Schema::new_member(
1456            shape_id!("test", "Input", "document"),
1457            ShapeType::Document,
1458            "document",
1459            0,
1460        );
1461        let string = Schema::new_member(
1462            shape_id!("test", "Input", "string"),
1463            ShapeType::String,
1464            "string",
1465            1,
1466        );
1467        let members = [&document, &string];
1468        let schema = Schema::new_struct(shape_id!("test", "Input"), ShapeType::Structure, &members);
1469        for (body, expected) in [
1470            (
1471                r#"{"document":null,"string":null,"unknown":null}"#,
1472                Some(Document::Null),
1473            ),
1474            (r#"{"string":null,"unknown":null}"#, None),
1475            (r#"{"document":true}"#, Some(Document::Bool(true))),
1476        ] {
1477            let mut actual = None;
1478            let mut deserializer =
1479                JsonDeserializer::new(body.as_bytes(), Arc::new(JsonCodecSettings::default()));
1480            deserializer
1481                .read_struct(&schema, &mut |member, value| {
1482                    assert_eq!(member.member_index(), Some(0), "{body}");
1483                    actual = Some(value.read_document(member)?);
1484                    Ok(())
1485                })
1486                .unwrap();
1487            assert_eq!(actual, expected, "{body}");
1488        }
1489    }
1490
1491    #[test]
1492    fn empty_integer_part_allowance_matches_legacy_negative_fractions() {
1493        for allow in [false, true] {
1494            for trailing in [false, true] {
1495                let settings = Arc::new(
1496                    JsonCodecSettings::builder()
1497                        .enforce_strictness(true)
1498                        .allow_empty_integer_part(allow)
1499                        .allow_trailing_decimal_point(trailing)
1500                        .build(),
1501                );
1502                for (text, expected) in [
1503                    ("-.123", Some(-0.123)),
1504                    ("-.123e2", Some(-12.3)),
1505                    ("-.0", Some(-0.0)),
1506                    ("-0.123", Some(-0.123)),
1507                    (".123", None),
1508                    ("-.", None),
1509                    ("-.e2", None),
1510                    ("-.123e", None),
1511                    ("-.123x", None),
1512                ] {
1513                    let accepted = expected.is_some() && (allow || text == "-0.123");
1514                    let result = JsonDeserializer::new(text.as_bytes(), settings.clone())
1515                        .read_double(dummy_schema());
1516                    assert_eq!(
1517                        result.is_ok(),
1518                        accepted,
1519                        "{text}, allow={allow}, trailing={trailing}"
1520                    );
1521                    if accepted {
1522                        assert_eq!(result.unwrap(), expected.unwrap());
1523                    }
1524                    let body = format!(r#"{{"ignored":{text}}}"#);
1525                    assert_eq!(
1526                        JsonDeserializer::new(body.as_bytes(), settings.clone())
1527                            .read_struct(dummy_schema(), &mut |_, _| Ok(()))
1528                            .is_ok(),
1529                        accepted,
1530                        "skipped {text}, allow={allow}, trailing={trailing}",
1531                    );
1532                }
1533            }
1534        }
1535        assert!(!JsonCodecSettings::default().allow_empty_integer_part);
1536        assert!(
1537            !JsonCodecSettings::builder()
1538                .build()
1539                .allow_empty_integer_part
1540        );
1541    }
1542
1543    #[test]
1544    fn number_allowances_preserve_other_strict_checks() {
1545        for leading in [false, true] {
1546            for trailing in [false, true] {
1547                let settings = Arc::new(
1548                    JsonCodecSettings::builder()
1549                        .enforce_strictness(true)
1550                        .allow_integral_float_numbers(true)
1551                        .allow_leading_zeros(leading)
1552                        .allow_trailing_decimal_point(trailing)
1553                        .build(),
1554                );
1555                for (text, value, accepted) in [
1556                    ("214748364.", 214748364, trailing),
1557                    ("0147483648", 147483648, leading),
1558                    ("0214748364\r", 214748364, leading),
1559                    ("012.e2", 1200, leading && trailing),
1560                    ("12e2", 1200, true),
1561                ] {
1562                    let result = JsonDeserializer::new(text.as_bytes(), settings.clone())
1563                        .read_integer(dummy_schema());
1564                    assert_eq!(
1565                        result.is_ok(),
1566                        accepted,
1567                        "{text} leading={leading} trailing={trailing}"
1568                    );
1569                    if accepted {
1570                        assert_eq!(result.unwrap(), value);
1571                    }
1572                    let body = format!(r#"{{"ignored":{text}}}"#);
1573                    assert_eq!(
1574                        JsonDeserializer::new(body.as_bytes(), settings.clone())
1575                            .read_struct(dummy_schema(), &mut |_, _| Ok(()))
1576                            .is_ok(),
1577                        accepted
1578                    );
1579                    assert_eq!(
1580                        JsonDeserializer::new(text.as_bytes(), settings.clone())
1581                            .read_double(dummy_schema())
1582                            .is_ok(),
1583                        accepted
1584                    );
1585                    assert_eq!(
1586                        JsonDeserializer::new(text.as_bytes(), settings.clone())
1587                            .read_document(dummy_schema())
1588                            .is_ok(),
1589                        accepted
1590                    );
1591                }
1592                for text in [
1593                    "1e",
1594                    "1e+",
1595                    "1..0",
1596                    "1e2e3",
1597                    "--1",
1598                    "+1",
1599                    "2147483648",
1600                    "1.5",
1601                ] {
1602                    assert!(
1603                        JsonDeserializer::new(text.as_bytes(), settings.clone())
1604                            .read_integer(dummy_schema())
1605                            .is_err(),
1606                        "{text}"
1607                    );
1608                }
1609                assert!(JsonDeserializer::new(b"\"raw\ncontrol\"", settings.clone())
1610                    .read_string(dummy_schema())
1611                    .is_err());
1612                assert!(JsonDeserializer::new(b"{} garbage", settings)
1613                    .read_struct(dummy_schema(), &mut |_, _| Ok(()))
1614                    .is_err());
1615            }
1616        }
1617    }
1618
1619    #[test]
1620    fn strict_strings_and_lenient_defaults() {
1621        for input in [b"\"raw\ncontrol\"".as_slice(), b"\"raw\x00control\""] {
1622            let mut lenient = JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()));
1623            assert!(lenient.read_string(dummy_schema()).is_ok());
1624            for key in [false, true] {
1625                let mut strict = JsonDeserializer::new(
1626                    input,
1627                    Arc::new(
1628                        JsonCodecSettings::builder()
1629                            .enforce_strictness(true)
1630                            .build(),
1631                    ),
1632                );
1633                assert!(if key {
1634                    strict.parse_key().map(|_| ())
1635                } else {
1636                    strict.read_string(dummy_schema()).map(|_| ())
1637                }
1638                .is_err());
1639                let mut strict = JsonDeserializer::new(
1640                    input,
1641                    Arc::new(
1642                        JsonCodecSettings::builder()
1643                            .enforce_strictness(true)
1644                            .build(),
1645                    ),
1646                );
1647                assert!(strict.skip_string().is_ok());
1648            }
1649        }
1650        for input in [b"\"ok\\n\\u0041\"".as_slice(), "\"hello 世界\"".as_bytes()] {
1651            let mut strict = JsonDeserializer::new(
1652                input,
1653                Arc::new(
1654                    JsonCodecSettings::builder()
1655                        .enforce_strictness(true)
1656                        .build(),
1657                ),
1658            );
1659            assert!(strict.skip_string().is_ok());
1660        }
1661        for strict in [false, true] {
1662            let settings = Arc::new(
1663                JsonCodecSettings::builder()
1664                    .enforce_strictness(strict)
1665                    .build(),
1666            );
1667            for input in [b"\"\xff\"".as_slice(), b"\"\xc0\x80\""] {
1668                assert!(JsonDeserializer::new(input, settings.clone())
1669                    .read_string(dummy_schema())
1670                    .is_err());
1671                assert!(JsonDeserializer::new(input, settings.clone())
1672                    .parse_key()
1673                    .is_err());
1674            }
1675        }
1676        let bad = b"\"\xff\"";
1677        assert!(
1678            JsonDeserializer::new(bad, Arc::new(JsonCodecSettings::default()))
1679                .skip_string()
1680                .is_ok()
1681        );
1682        assert!(JsonDeserializer::new(
1683            bad,
1684            Arc::new(
1685                JsonCodecSettings::builder()
1686                    .enforce_strictness(true)
1687                    .build()
1688            )
1689        )
1690        .skip_string()
1691        .is_ok());
1692    }
1693
1694    #[test]
1695    fn parse_integral_decimal_exact_cases() {
1696        for (input, expected) in [
1697            ("1.0", Some(1)),
1698            ("1e3", Some(1000)),
1699            ("12.0", Some(12)),
1700            ("100E0", Some(100)),
1701            ("1.50E1", Some(15)),
1702            ("1234567890.000", Some(1234567890)),
1703            ("0.5e1", Some(5)),
1704            ("0.05e2", Some(5)),
1705            ("100e-2", Some(1)),
1706            ("12300e-2", Some(123)),
1707            ("1.23e2", Some(123)),
1708            ("0.0", Some(0)),
1709            ("-0e5", Some(0)),
1710            ("0e-5", Some(0)),
1711            ("0e400", Some(0)),
1712            ("-123456789012345e3", Some(-123456789012345000)),
1713            ("9007199254740993.0", Some(9007199254740993)),
1714            ("9223372036854775807e0", Some(i64::MAX)),
1715            ("-9223372036854775808.0", Some(i64::MIN)),
1716            ("1.5", None),
1717            ("2.5e0", None),
1718            ("123e-2", None),
1719            ("1e-5", None),
1720            ("1e-400", None),
1721            ("1e19", None),
1722            ("1e30", None),
1723            ("9223372036854775808.0", None),
1724            ("-9223372036854775809e0", None),
1725            ("0e999999999999999999999", None),
1726        ] {
1727            assert_eq!(parse_integral_decimal(input).ok(), expected, "{input}");
1728        }
1729    }
1730
1731    #[test]
1732    fn exact_integral_numbers_and_strict_grammar() {
1733        let settings = Arc::new(
1734            JsonCodecSettings::builder()
1735                .enforce_strictness(true)
1736                .allow_integral_float_numbers(true)
1737                .build(),
1738        );
1739        for (input, expected) in [
1740            ("1.0", 1),
1741            ("1e3", 1000),
1742            ("1.20e1", 12),
1743            ("9007199254740993", 9007199254740993),
1744            ("9223372036854775807.0", i64::MAX),
1745            ("-9223372036854775808e0", i64::MIN),
1746        ] {
1747            assert_eq!(
1748                JsonDeserializer::new(input.as_bytes(), settings.clone())
1749                    .read_long(dummy_schema())
1750                    .unwrap(),
1751                expected,
1752                "{input}"
1753            );
1754        }
1755        for input in [
1756            "0e999999999999999999999",
1757            "1.01",
1758            "1e-3",
1759            "9223372036854775808.0",
1760            "-9223372036854775809.0",
1761            "1e10000",
1762            "12.",
1763            "01",
1764            "+1",
1765            "1e",
1766            "--1",
1767            "1e+",
1768            "1.0.0",
1769        ] {
1770            assert!(
1771                JsonDeserializer::new(input.as_bytes(), settings.clone())
1772                    .read_long(dummy_schema())
1773                    .is_err(),
1774                "{input}"
1775            );
1776        }
1777        for input in ["12.", "01", "+1", "1e", "1e+", "--1", "1.0.0"] {
1778            assert!(
1779                JsonDeserializer::new(input.as_bytes(), settings.clone())
1780                    .read_double(dummy_schema())
1781                    .is_err(),
1782                "{input}"
1783            );
1784        }
1785        // With defaults, an integer member rejects a floating-point value as a
1786        // type mismatch instead of truncating it.
1787        assert!(matches!(
1788            JsonDeserializer::new(b"1.0", Arc::new(JsonCodecSettings::default()))
1789                .read_long(dummy_schema()),
1790            Err(SerdeError::TypeMismatch { .. })
1791        ));
1792        assert_eq!(
1793            JsonDeserializer::new(b"12.", Arc::new(JsonCodecSettings::default()))
1794                .read_double(dummy_schema())
1795                .unwrap(),
1796            12.0
1797        );
1798        for strict in [false, true] {
1799            let settings = Arc::new(
1800                JsonCodecSettings::builder()
1801                    .enforce_strictness(strict)
1802                    .allow_integral_float_numbers(true)
1803                    .build(),
1804            );
1805            assert_eq!(
1806                JsonDeserializer::new(b"127.0", settings.clone())
1807                    .read_byte(dummy_schema())
1808                    .unwrap(),
1809                127
1810            );
1811            assert!(JsonDeserializer::new(b"128.0", settings.clone())
1812                .read_byte(dummy_schema())
1813                .is_err());
1814            assert!(JsonDeserializer::new(b"32768e0", settings.clone())
1815                .read_short(dummy_schema())
1816                .is_err());
1817            assert!(JsonDeserializer::new(b"2147483648.0", settings.clone())
1818                .read_integer(dummy_schema())
1819                .is_err());
1820            assert!(JsonDeserializer::new(b"12.", settings)
1821                .read_long(dummy_schema())
1822                .is_err());
1823        }
1824        for input in ["1e999", "-1e999", "9223372036854775808.0"] {
1825            assert!(JsonDeserializer::new(input.as_bytes(), settings.clone())
1826                .read_timestamp(dummy_schema())
1827                .is_err());
1828        }
1829        for (input, valid) in [(b"".as_slice(), true), (b" \n", false), (b"{}", true)] {
1830            assert_eq!(
1831                JsonDeserializer::new(input, settings.clone())
1832                    .read_struct(dummy_schema(), &mut |_, _| Ok(()))
1833                    .is_ok(),
1834                valid
1835            );
1836        }
1837    }
1838
1839    fn dummy_schema() -> &'static aws_smithy_schema::Schema<'static> {
1840        &aws_smithy_schema::prelude::STRING
1841    }
1842
1843    #[test]
1844    fn test_read_boolean() {
1845        let mut deser = JsonDeserializer::new(b"true", Arc::new(JsonCodecSettings::default()));
1846        assert!(deser.read_boolean(dummy_schema()).unwrap());
1847
1848        let mut deser = JsonDeserializer::new(b"false", Arc::new(JsonCodecSettings::default()));
1849        assert!(!(deser.read_boolean(dummy_schema()).unwrap()));
1850    }
1851
1852    #[test]
1853    fn test_read_integer() {
1854        let mut deser = JsonDeserializer::new(b"42", Arc::new(JsonCodecSettings::default()));
1855        assert_eq!(deser.read_integer(dummy_schema()).unwrap(), 42);
1856
1857        let mut deser = JsonDeserializer::new(b"-123", Arc::new(JsonCodecSettings::default()));
1858        assert_eq!(deser.read_integer(dummy_schema()).unwrap(), -123);
1859    }
1860
1861    #[test]
1862    fn test_read_long() {
1863        let mut deser = JsonDeserializer::new(
1864            b"9223372036854775807",
1865            Arc::new(JsonCodecSettings::default()),
1866        );
1867        assert_eq!(deser.read_long(dummy_schema()).unwrap(), i64::MAX);
1868    }
1869
1870    #[test]
1871    fn test_read_float() {
1872        let mut deser = JsonDeserializer::new(b"3.15", Arc::new(JsonCodecSettings::default()));
1873        assert!((deser.read_float(dummy_schema()).unwrap() - 3.15).abs() < 0.01);
1874    }
1875
1876    #[test]
1877    fn test_read_double() {
1878        let mut deser = JsonDeserializer::new(b"2.72", Arc::new(JsonCodecSettings::default()));
1879        assert!((deser.read_double(dummy_schema()).unwrap() - 2.72).abs() < 0.001);
1880    }
1881
1882    #[test]
1883    fn test_read_string() {
1884        let mut deser =
1885            JsonDeserializer::new(br#""hello world""#, Arc::new(JsonCodecSettings::default()));
1886        assert_eq!(deser.read_string(dummy_schema()).unwrap(), "hello world");
1887
1888        let mut deser =
1889            JsonDeserializer::new(br#""hello\nworld""#, Arc::new(JsonCodecSettings::default()));
1890        assert_eq!(deser.read_string(dummy_schema()).unwrap(), "hello\nworld");
1891    }
1892
1893    #[test]
1894    fn test_is_null() {
1895        let deser = JsonDeserializer::new(b"null", Arc::new(JsonCodecSettings::default()));
1896        assert!(deser.is_null());
1897
1898        let deser = JsonDeserializer::new(b"42", Arc::new(JsonCodecSettings::default()));
1899        assert!(!deser.is_null());
1900    }
1901
1902    #[test]
1903    fn test_read_byte_range() {
1904        let mut deser = JsonDeserializer::new(b"127", Arc::new(JsonCodecSettings::default()));
1905        assert_eq!(deser.read_byte(dummy_schema()).unwrap(), 127);
1906
1907        let mut deser = JsonDeserializer::new(b"128", Arc::new(JsonCodecSettings::default()));
1908        assert!(deser.read_byte(dummy_schema()).is_err());
1909    }
1910
1911    #[test]
1912    fn test_read_struct() {
1913        use aws_smithy_schema::Schema;
1914
1915        #[derive(Debug, Default, PartialEq)]
1916        struct Person {
1917            first_name: String,
1918            last_name: String,
1919            age: i32,
1920        }
1921
1922        static FIRST_NAME: Schema<'static> = Schema::new_member(
1923            aws_smithy_schema::shape_id!("test", "Person"),
1924            aws_smithy_schema::ShapeType::String,
1925            "firstName",
1926            0,
1927        );
1928        static LAST_NAME: Schema<'static> = Schema::new_member(
1929            aws_smithy_schema::shape_id!("test", "Person"),
1930            aws_smithy_schema::ShapeType::String,
1931            "lastName",
1932            1,
1933        );
1934        static AGE: Schema<'static> = Schema::new_member(
1935            aws_smithy_schema::shape_id!("test", "Person"),
1936            aws_smithy_schema::ShapeType::Integer,
1937            "age",
1938            2,
1939        );
1940        static PERSON_SCHEMA: Schema<'static> = Schema::new_struct(
1941            aws_smithy_schema::shape_id!("test", "Person"),
1942            aws_smithy_schema::ShapeType::Structure,
1943            &[&FIRST_NAME, &LAST_NAME, &AGE],
1944        );
1945
1946        fn consume_person(
1947            person: &mut Person,
1948            schema: &Schema<'_>,
1949            deser: &mut dyn ShapeDeserializer,
1950        ) -> Result<(), SerdeError> {
1951            match schema.member_name() {
1952                Some("firstName") => person.first_name = deser.read_string(schema)?,
1953                Some("lastName") => person.last_name = deser.read_string(schema)?,
1954                Some("age") => person.age = deser.read_integer(schema)?,
1955                _ => {}
1956            }
1957            Ok(())
1958        }
1959
1960        let json = br#"{"lastName":"Smithy","firstName":"Alice","age":30}"#;
1961        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1962        let mut person = Person::default();
1963        deser
1964            .read_struct(&PERSON_SCHEMA, &mut |member, d| {
1965                consume_person(&mut person, member, d)
1966            })
1967            .unwrap();
1968        assert_eq!(
1969            person,
1970            Person {
1971                first_name: "Alice".to_string(),
1972                last_name: "Smithy".to_string(),
1973                age: 30
1974            }
1975        );
1976
1977        let json =
1978            br#"{"firstName":          "Alice","age":12345678,     "lastName":"\"Smithy\""}"#;
1979        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1980        let mut person = Person::default();
1981        deser
1982            .read_struct(&PERSON_SCHEMA, &mut |member, d| {
1983                consume_person(&mut person, member, d)
1984            })
1985            .unwrap();
1986        assert_eq!(
1987            person,
1988            Person {
1989                first_name: "Alice".to_string(),
1990                last_name: "\"Smithy\"".to_string(),
1991                age: 12345678
1992            }
1993        );
1994    }
1995
1996    #[test]
1997    fn test_read_list() {
1998        let json = b"[1, 2, 3, 4, 5]";
1999        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2000        let capacity = deser.container_size().unwrap_or(0);
2001        let mut result = Vec::with_capacity(capacity);
2002        let allocated_capacity = result.capacity();
2003        deser
2004            .read_list(dummy_schema(), &mut |deser| {
2005                result.push(deser.read_integer(dummy_schema())?);
2006                Ok(())
2007            })
2008            .unwrap();
2009        assert_eq!(result, vec![1, 2, 3, 4, 5]);
2010        // Ensure no more memory was allocated for the container
2011        assert_eq!(result.capacity(), allocated_capacity);
2012
2013        let json = b"[]";
2014        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2015        let capacity = deser.container_size().unwrap_or(0);
2016        let mut result = Vec::<i32>::with_capacity(capacity);
2017        let allocated_capacity = result.capacity();
2018        deser
2019            .read_list(dummy_schema(), &mut |deser| {
2020                result.push(deser.read_integer(dummy_schema())?);
2021                Ok(())
2022            })
2023            .unwrap();
2024        assert_eq!(result, Vec::<i32>::new());
2025        // Ensure no more memory was allocated for the container
2026        assert_eq!(result.capacity(), allocated_capacity);
2027
2028        let json = br#"["hello", "world"]"#;
2029        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2030        let capacity = deser.container_size().unwrap_or(0);
2031        let mut result = Vec::with_capacity(capacity);
2032        let allocated_capacity = result.capacity();
2033        deser
2034            .read_list(dummy_schema(), &mut |deser| {
2035                result.push(deser.read_string(dummy_schema())?);
2036                Ok(())
2037            })
2038            .unwrap();
2039        assert_eq!(result, vec!["hello", "world"]);
2040        // Ensure no more memory was allocated for the container
2041        assert_eq!(result.capacity(), allocated_capacity);
2042    }
2043
2044    #[test]
2045    fn test_container_size() {
2046        let deser =
2047            JsonDeserializer::new(b"[1, 2, 3, 4, 5]", Arc::new(JsonCodecSettings::default()));
2048        assert_eq!(deser.container_size(), Some(5));
2049
2050        let deser = JsonDeserializer::new(b"[]", Arc::new(JsonCodecSettings::default()));
2051        assert_eq!(deser.container_size(), Some(0));
2052
2053        let deser = JsonDeserializer::new(
2054            br#"{"a": 1, "b": 2, "c": 3}"#,
2055            Arc::new(JsonCodecSettings::default()),
2056        );
2057        assert_eq!(deser.container_size(), Some(3));
2058
2059        let deser = JsonDeserializer::new(b"{}", Arc::new(JsonCodecSettings::default()));
2060        assert_eq!(deser.container_size(), Some(0));
2061
2062        let deser = JsonDeserializer::new(
2063            b"[[1, 2], [3, 4], [5, 6]]",
2064            Arc::new(JsonCodecSettings::default()),
2065        );
2066        assert_eq!(deser.container_size(), Some(3));
2067
2068        let deser = JsonDeserializer::new(b"42", Arc::new(JsonCodecSettings::default()));
2069        assert_eq!(deser.container_size(), None);
2070    }
2071
2072    #[test]
2073    fn test_read_map() {
2074        use std::collections::HashMap;
2075
2076        let json = br#"{"a": 1, "b": 2, "c": 3}"#;
2077        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2078        let calculated_capacity = deser.container_size().unwrap_or(0);
2079        let mut result = HashMap::with_capacity(calculated_capacity);
2080        let allocated_capacity = result.capacity();
2081        deser
2082            .read_map(dummy_schema(), &mut |key, deser| {
2083                result.insert(key, deser.read_integer(dummy_schema())?);
2084                Ok(())
2085            })
2086            .unwrap();
2087        assert_eq!(result.len(), 3);
2088        assert_eq!(result.get("a"), Some(&1));
2089        assert_eq!(result.get("b"), Some(&2));
2090        assert_eq!(result.get("c"), Some(&3));
2091        // Ensure no more memory was allocated for the container
2092        assert_eq!(result.capacity(), allocated_capacity);
2093
2094        let json = b"{}";
2095        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2096        let calculated_capacity = deser.container_size().unwrap_or(0);
2097        let mut result = HashMap::<String, i32>::with_capacity(calculated_capacity);
2098        let allocated_capacity = result.capacity();
2099        deser
2100            .read_map(dummy_schema(), &mut |key, deser| {
2101                result.insert(key, deser.read_integer(dummy_schema())?);
2102                Ok(())
2103            })
2104            .unwrap();
2105        assert_eq!(result, HashMap::<String, i32>::new());
2106        // Ensure no more memory was allocated for the container
2107        assert_eq!(result.capacity(), allocated_capacity);
2108
2109        let json = br#"{"name": "Alice", "city": "Seattle"}"#;
2110        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2111        let calculated_capacity = deser.container_size().unwrap_or(0);
2112        let mut result = HashMap::with_capacity(calculated_capacity);
2113        let allocated_capacity = result.capacity();
2114        deser
2115            .read_map(dummy_schema(), &mut |key, deser| {
2116                result.insert(key, deser.read_string(dummy_schema())?);
2117                Ok(())
2118            })
2119            .unwrap();
2120        assert_eq!(result.len(), 2);
2121        assert_eq!(result.get("name"), Some(&"Alice".to_string()));
2122        assert_eq!(result.get("city"), Some(&"Seattle".to_string()));
2123        // Ensure no more memory was allocated for the container
2124        assert_eq!(result.capacity(), allocated_capacity);
2125    }
2126
2127    #[test]
2128    fn test_nested_complex_deserialization() {
2129        use aws_smithy_schema::Schema;
2130        use std::collections::HashMap;
2131
2132        #[derive(Debug, Default, PartialEq)]
2133        struct Address {
2134            street: String,
2135            city: String,
2136            zip: i32,
2137        }
2138        #[derive(Debug, Default, PartialEq)]
2139        struct Company {
2140            name: String,
2141            employees: Vec<String>,
2142            metadata: HashMap<String, i32>,
2143            active: bool,
2144        }
2145        #[derive(Debug, Default, PartialEq)]
2146        struct User {
2147            id: i64,
2148            name: String,
2149            scores: Vec<f64>,
2150            address: Address,
2151            companies: Vec<Company>,
2152            tags: HashMap<String, String>,
2153        }
2154
2155        // Address members & schema
2156        static ADDR_STREET: Schema<'static> = Schema::new_member(
2157            aws_smithy_schema::shape_id!("test", "Address"),
2158            aws_smithy_schema::ShapeType::String,
2159            "street",
2160            0,
2161        );
2162        static ADDR_CITY: Schema<'static> = Schema::new_member(
2163            aws_smithy_schema::shape_id!("test", "Address"),
2164            aws_smithy_schema::ShapeType::String,
2165            "city",
2166            1,
2167        );
2168        static ADDR_ZIP: Schema<'static> = Schema::new_member(
2169            aws_smithy_schema::shape_id!("test", "Address"),
2170            aws_smithy_schema::ShapeType::Integer,
2171            "zip",
2172            2,
2173        );
2174        static ADDRESS_SCHEMA: Schema<'static> = Schema::new_struct(
2175            aws_smithy_schema::shape_id!("test", "Address"),
2176            aws_smithy_schema::ShapeType::Structure,
2177            &[&ADDR_STREET, &ADDR_CITY, &ADDR_ZIP],
2178        );
2179
2180        // Company members & schema
2181        static COMP_NAME: Schema<'static> = Schema::new_member(
2182            aws_smithy_schema::shape_id!("test", "Company"),
2183            aws_smithy_schema::ShapeType::String,
2184            "name",
2185            0,
2186        );
2187        static COMP_EMPLOYEES: Schema<'static> = Schema::new_member(
2188            aws_smithy_schema::shape_id!("test", "Company"),
2189            aws_smithy_schema::ShapeType::List,
2190            "employees",
2191            1,
2192        );
2193        static COMP_METADATA: Schema<'static> = Schema::new_member(
2194            aws_smithy_schema::shape_id!("test", "Company"),
2195            aws_smithy_schema::ShapeType::Map,
2196            "metadata",
2197            2,
2198        );
2199        static COMP_ACTIVE: Schema<'static> = Schema::new_member(
2200            aws_smithy_schema::shape_id!("test", "Company"),
2201            aws_smithy_schema::ShapeType::Boolean,
2202            "active",
2203            3,
2204        );
2205        static COMPANY_SCHEMA: Schema<'static> = Schema::new_struct(
2206            aws_smithy_schema::shape_id!("test", "Company"),
2207            aws_smithy_schema::ShapeType::Structure,
2208            &[&COMP_NAME, &COMP_EMPLOYEES, &COMP_METADATA, &COMP_ACTIVE],
2209        );
2210
2211        // User members & schema
2212        static USER_ID: Schema<'static> = Schema::new_member(
2213            aws_smithy_schema::shape_id!("test", "User"),
2214            aws_smithy_schema::ShapeType::Long,
2215            "id",
2216            0,
2217        );
2218        static USER_NAME: Schema<'static> = Schema::new_member(
2219            aws_smithy_schema::shape_id!("test", "User"),
2220            aws_smithy_schema::ShapeType::String,
2221            "name",
2222            1,
2223        );
2224        static USER_SCORES: Schema<'static> = Schema::new_member(
2225            aws_smithy_schema::shape_id!("test", "User"),
2226            aws_smithy_schema::ShapeType::List,
2227            "scores",
2228            2,
2229        );
2230        static USER_ADDRESS: Schema<'static> = Schema::new_member(
2231            aws_smithy_schema::shape_id!("test", "User"),
2232            aws_smithy_schema::ShapeType::Structure,
2233            "address",
2234            3,
2235        );
2236        static USER_COMPANIES: Schema<'static> = Schema::new_member(
2237            aws_smithy_schema::shape_id!("test", "User"),
2238            aws_smithy_schema::ShapeType::List,
2239            "companies",
2240            4,
2241        );
2242        static USER_TAGS: Schema<'static> = Schema::new_member(
2243            aws_smithy_schema::shape_id!("test", "User"),
2244            aws_smithy_schema::ShapeType::Map,
2245            "tags",
2246            5,
2247        );
2248        static USER_SCHEMA: Schema<'static> = Schema::new_struct(
2249            aws_smithy_schema::shape_id!("test", "User"),
2250            aws_smithy_schema::ShapeType::Structure,
2251            &[
2252                &USER_ID,
2253                &USER_NAME,
2254                &USER_SCORES,
2255                &USER_ADDRESS,
2256                &USER_COMPANIES,
2257                &USER_TAGS,
2258            ],
2259        );
2260
2261        fn consume_address(
2262            addr: &mut Address,
2263            schema: &Schema<'_>,
2264            deser: &mut dyn ShapeDeserializer,
2265        ) -> Result<(), SerdeError> {
2266            match schema.member_name() {
2267                Some("street") => addr.street = deser.read_string(schema)?,
2268                Some("city") => addr.city = deser.read_string(schema)?,
2269                Some("zip") => addr.zip = deser.read_integer(schema)?,
2270                _ => {}
2271            }
2272            Ok(())
2273        }
2274
2275        fn consume_company(
2276            comp: &mut Company,
2277            schema: &Schema<'_>,
2278            deser: &mut dyn ShapeDeserializer,
2279        ) -> Result<(), SerdeError> {
2280            match schema.member_name() {
2281                Some("name") => comp.name = deser.read_string(schema)?,
2282                Some("active") => comp.active = deser.read_boolean(schema)?,
2283                Some("employees") => {
2284                    let mut v = Vec::new();
2285                    deser.read_list(schema, &mut |d| {
2286                        v.push(d.read_string(dummy_schema())?);
2287                        Ok(())
2288                    })?;
2289                    comp.employees = v;
2290                }
2291                Some("metadata") => {
2292                    let mut m = HashMap::new();
2293                    deser.read_map(schema, &mut |k, d| {
2294                        m.insert(k, d.read_integer(dummy_schema())?);
2295                        Ok(())
2296                    })?;
2297                    comp.metadata = m;
2298                }
2299                _ => {}
2300            }
2301            Ok(())
2302        }
2303
2304        fn consume_user(
2305            user: &mut User,
2306            schema: &Schema<'_>,
2307            deser: &mut dyn ShapeDeserializer,
2308        ) -> Result<(), SerdeError> {
2309            match schema.member_name() {
2310                Some("id") => user.id = deser.read_long(schema)?,
2311                Some("name") => user.name = deser.read_string(schema)?,
2312                Some("scores") => {
2313                    let mut v = Vec::new();
2314                    deser.read_list(schema, &mut |d| {
2315                        v.push(d.read_double(dummy_schema())?);
2316                        Ok(())
2317                    })?;
2318                    user.scores = v;
2319                }
2320                Some("address") => {
2321                    let mut addr = Address::default();
2322                    deser.read_struct(&ADDRESS_SCHEMA, &mut |member, d| {
2323                        consume_address(&mut addr, member, d)
2324                    })?;
2325                    user.address = addr;
2326                }
2327                Some("companies") => {
2328                    let mut v = Vec::new();
2329                    deser.read_list(schema, &mut |d| {
2330                        let mut comp = Company::default();
2331                        d.read_struct(&COMPANY_SCHEMA, &mut |member, d| {
2332                            consume_company(&mut comp, member, d)
2333                        })?;
2334                        v.push(comp);
2335                        Ok(())
2336                    })?;
2337                    user.companies = v;
2338                }
2339                Some("tags") => {
2340                    let mut m = HashMap::new();
2341                    deser.read_map(schema, &mut |k, d| {
2342                        m.insert(k, d.read_string(dummy_schema())?);
2343                        Ok(())
2344                    })?;
2345                    user.tags = m;
2346                }
2347                _ => {}
2348            }
2349            Ok(())
2350        }
2351
2352        let json = br#"{
2353            "id": 12345,
2354            "name": "John Doe",
2355            "scores": [95.5, 87.3, 92.1],
2356            "address": {
2357                "street": "123 Main St",
2358                "city": "Seattle",
2359                "zip": 98101
2360            },
2361            "companies": [
2362                {
2363                    "name": "TechCorp",
2364                    "employees": ["Alice", "Bob"],
2365                    "metadata": {"founded": 2010, "size": 500},
2366                    "active": true
2367                },
2368                {
2369                    "name": "StartupInc",
2370                    "employees": ["Charlie"],
2371                    "metadata": {"founded": 2020},
2372                    "active": false
2373                }
2374            ],
2375            "tags": {"role": "admin", "level": "senior"}
2376        }"#;
2377
2378        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2379        let mut user = User::default();
2380        deser
2381            .read_struct(&USER_SCHEMA, &mut |member, d| {
2382                consume_user(&mut user, member, d)
2383            })
2384            .unwrap();
2385
2386        assert_eq!(user.id, 12345);
2387        assert_eq!(user.name, "John Doe");
2388        assert_eq!(user.scores, vec![95.5, 87.3, 92.1]);
2389        assert_eq!(user.address.street, "123 Main St");
2390        assert_eq!(user.address.city, "Seattle");
2391        assert_eq!(user.address.zip, 98101);
2392        assert_eq!(user.companies.len(), 2);
2393        assert_eq!(user.companies[0].name, "TechCorp");
2394        assert_eq!(user.companies[0].employees, vec!["Alice", "Bob"]);
2395        assert_eq!(user.companies[0].metadata.get("founded"), Some(&2010));
2396        assert_eq!(user.companies[0].metadata.get("size"), Some(&500));
2397        assert!(user.companies[0].active);
2398        assert_eq!(user.companies[1].name, "StartupInc");
2399        assert_eq!(user.companies[1].employees, vec!["Charlie"]);
2400        assert_eq!(user.companies[1].metadata.get("founded"), Some(&2020));
2401        assert!(!user.companies[1].active);
2402        assert_eq!(user.tags.get("role"), Some(&"admin".to_string()));
2403        assert_eq!(user.tags.get("level"), Some(&"senior".to_string()));
2404    }
2405
2406    #[test]
2407    fn test_json_name_deserialization() {
2408        use aws_smithy_schema::Schema;
2409
2410        static FOO_MEMBER: Schema<'static> = Schema::new_member(
2411            aws_smithy_schema::shape_id!("test", "MyStruct"),
2412            aws_smithy_schema::ShapeType::String,
2413            "foo",
2414            0,
2415        );
2416        // "bar" member has @jsonName("Baz")
2417        static BAR_MEMBER: Schema<'static> = Schema::new_member(
2418            aws_smithy_schema::shape_id!("test", "MyStruct"),
2419            aws_smithy_schema::ShapeType::Integer,
2420            "bar",
2421            1,
2422        )
2423        .with_json_name("Baz");
2424        static STRUCT_SCHEMA: Schema<'static> = Schema::new_struct(
2425            aws_smithy_schema::shape_id!("test", "MyStruct"),
2426            aws_smithy_schema::ShapeType::Structure,
2427            &[&FOO_MEMBER, &BAR_MEMBER],
2428        );
2429
2430        let json = br#"{"foo":"hello","Baz":42}"#;
2431
2432        // With use_json_name=true, "Baz" resolves to the "bar" member
2433        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2434        let (mut foo, mut bar) = (None::<String>, None::<i32>);
2435        deser
2436            .read_struct(&STRUCT_SCHEMA, &mut |member, d| {
2437                match member.member_name() {
2438                    Some("foo") => foo = Some(d.read_string(member)?),
2439                    Some("bar") => bar = Some(d.read_integer(member)?),
2440                    _ => {}
2441                }
2442                Ok(())
2443            })
2444            .unwrap();
2445        assert_eq!(foo.as_deref(), Some("hello"));
2446        assert_eq!(bar, Some(42));
2447
2448        // With use_json_name=false, "Baz" is unknown and gets skipped
2449        let mut deser = JsonDeserializer::new(
2450            json,
2451            Arc::new(JsonCodecSettings::builder().use_json_name(false).build()),
2452        );
2453        let (mut foo, mut bar) = (None::<String>, None::<i32>);
2454        deser
2455            .read_struct(&STRUCT_SCHEMA, &mut |member, d| {
2456                match member.member_name() {
2457                    Some("foo") => foo = Some(d.read_string(member)?),
2458                    Some("bar") => bar = Some(d.read_integer(member)?),
2459                    _ => {}
2460                }
2461                Ok(())
2462            })
2463            .unwrap();
2464        assert_eq!(foo.as_deref(), Some("hello"));
2465        assert_eq!(bar, None); // "Baz" not recognized without jsonName
2466    }
2467
2468    /// A member's own name can collide with another member's `@jsonName`. Every
2469    /// occurrence of the shared field resolves to the first matching member, wherever it
2470    /// appears in the object and however often it repeats.
2471    #[test]
2472    fn colliding_wire_names_resolve_to_the_first_member() {
2473        use aws_smithy_schema::{shape_id, Schema, ShapeType};
2474
2475        static STATUS: Schema<'static> = Schema::new_member(
2476            shape_id!("ns", "S", "status"),
2477            ShapeType::String,
2478            "status",
2479            0,
2480        );
2481        static STATUS_TEXT: Schema<'static> = Schema::new_member(
2482            shape_id!("ns", "S", "statusText"),
2483            ShapeType::String,
2484            "statusText",
2485            1,
2486        )
2487        .with_json_name("status");
2488        static OTHER: Schema<'static> =
2489            Schema::new_member(shape_id!("ns", "S", "other"), ShapeType::String, "other", 2);
2490        static S: Schema<'static> = Schema::new_struct(
2491            shape_id!("ns", "S"),
2492            ShapeType::Structure,
2493            &[&STATUS, &STATUS_TEXT, &OTHER],
2494        );
2495
2496        fn read(json: &[u8]) -> Vec<(usize, String)> {
2497            let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2498            let mut seen = Vec::new();
2499            deser
2500                .read_struct(&S, &mut |member, d| {
2501                    seen.push((member.member_index().unwrap(), d.read_string(member)?));
2502                    Ok(())
2503                })
2504                .unwrap();
2505            seen
2506        }
2507
2508        assert_eq!(
2509            read(br#"{"status":"a","status":"b"}"#),
2510            [(0, "a".to_owned()), (0, "b".to_owned())]
2511        );
2512        assert_eq!(
2513            read(br#"{"other":"x","status":"a"}"#),
2514            [(2, "x".to_owned()), (0, "a".to_owned())]
2515        );
2516        assert_eq!(
2517            read(br#"{"status":"a","other":"x","status":"b"}"#),
2518            [
2519                (0, "a".to_owned()),
2520                (2, "x".to_owned()),
2521                (0, "b".to_owned())
2522            ]
2523        );
2524    }
2525
2526    fn timestamp_schema() -> &'static aws_smithy_schema::Schema<'static> {
2527        &aws_smithy_schema::prelude::TIMESTAMP
2528    }
2529
2530    #[test]
2531    fn test_read_timestamp_positive_integer() {
2532        let mut deser =
2533            JsonDeserializer::new(b"1700000000", Arc::new(JsonCodecSettings::default()));
2534        let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2535        assert_eq!(ts, DateTime::from_secs(1700000000));
2536    }
2537
2538    #[test]
2539    fn test_read_timestamp_negative_integer() {
2540        let mut deser = JsonDeserializer::new(b"-1000", Arc::new(JsonCodecSettings::default()));
2541        let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2542        assert_eq!(ts, DateTime::from_secs(-1000));
2543    }
2544
2545    #[test]
2546    fn test_read_timestamp_float() {
2547        // This is the format DynamoDB uses: epoch seconds with fractional part
2548        let mut deser =
2549            JsonDeserializer::new(b"1.615218678973E9", Arc::new(JsonCodecSettings::default()));
2550        let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2551        assert_eq!(ts, DateTime::from_secs_f64(1.615218678973E9));
2552    }
2553
2554    #[test]
2555    fn test_read_timestamp_float_simple() {
2556        let mut deser =
2557            JsonDeserializer::new(b"1700000000.5", Arc::new(JsonCodecSettings::default()));
2558        let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2559        assert_eq!(ts, DateTime::from_secs_f64(1700000000.5));
2560    }
2561
2562    #[test]
2563    fn test_read_timestamp_string_datetime() {
2564        let mut deser = JsonDeserializer::new(
2565            br#""2023-11-14T22:13:20Z""#,
2566            Arc::new(JsonCodecSettings::default()),
2567        );
2568        let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2569        assert_eq!(ts, DateTime::from_secs(1700000000));
2570    }
2571
2572    #[test]
2573    fn test_read_timestamp_invalid() {
2574        let mut deser = JsonDeserializer::new(b"true", Arc::new(JsonCodecSettings::default()));
2575        assert!(deser.read_timestamp(timestamp_schema()).is_err());
2576    }
2577
2578    #[test]
2579    fn test_read_timestamp_string_honors_httpdate_default() {
2580        // No `@timestampFormat` trait on the schema, so the codec's default
2581        // format governs string parsing. With an http-date default, an
2582        // http-date string must parse. (Before L2a the string branch
2583        // hardcoded date-time and this errored.)
2584        let settings = JsonCodecSettings::builder()
2585            .default_timestamp_format(aws_smithy_types::date_time::Format::HttpDate)
2586            .build();
2587        let mut deser =
2588            JsonDeserializer::new(br#""Tue, 14 Nov 2023 22:13:20 GMT""#, Arc::new(settings));
2589        let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2590        assert_eq!(ts, DateTime::from_secs(1700000000));
2591    }
2592
2593    #[test]
2594    fn test_read_timestamp_string_default_epoch_still_parses_datetime() {
2595        // Regression guard for the common case: with the default
2596        // (epoch-seconds) format, a date-time *string* still parses via the
2597        // offset-aware fallback — the shared `string_timestamp_format` helper
2598        // must not break this.
2599        let mut deser = JsonDeserializer::new(
2600            br#""2023-11-14T22:13:20Z""#,
2601            Arc::new(JsonCodecSettings::default()),
2602        );
2603        let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2604        assert_eq!(ts, DateTime::from_secs(1700000000));
2605    }
2606
2607    #[test]
2608    fn test_read_timestamp_rejects_u64_overflow() {
2609        // A positive epoch-seconds value beyond i64::MAX must error rather
2610        // than silently wrapping through `as i64`.
2611        let big = (i64::MAX as u64 + 1).to_string();
2612        let mut deser =
2613            JsonDeserializer::new(big.as_bytes(), Arc::new(JsonCodecSettings::default()));
2614        let err = deser
2615            .read_timestamp(timestamp_schema())
2616            .expect_err("u64 epoch overflowing i64 must error");
2617        assert!(
2618            matches!(err, SerdeError::InvalidInput { .. }),
2619            "expected InvalidInput, got {err:?}"
2620        );
2621    }
2622
2623    #[test]
2624    fn test_read_integer_rejects_float() {
2625        // An integer member must reject a floating-point value rather than
2626        // reading the integer part and leaving the fraction in the stream.
2627        let mut deser = JsonDeserializer::new(b"1.5", Arc::new(JsonCodecSettings::default()));
2628        let err = deser
2629            .read_integer(&aws_smithy_schema::prelude::INTEGER)
2630            .expect_err("a float must not deserialize as an integer");
2631        assert!(
2632            matches!(err, SerdeError::TypeMismatch { .. }),
2633            "expected TypeMismatch, got {err:?}"
2634        );
2635    }
2636
2637    #[test]
2638    fn test_skip_value_empty_array() {
2639        // Regression: skip_value failed on [] because json_token_iter can't parse ']' as a value start
2640        use aws_smithy_schema::ShapeType;
2641        static KNOWN_MEMBER: Schema<'static> = Schema::new_member(
2642            aws_smithy_schema::shape_id!("test", "S"),
2643            ShapeType::String,
2644            "known",
2645            0,
2646        );
2647        static MEMBERS: &[&Schema<'_>] = &[&KNOWN_MEMBER];
2648        static TEST_SCHEMA: Schema<'static> = Schema::new_struct(
2649            aws_smithy_schema::shape_id!("test", "S"),
2650            ShapeType::Structure,
2651            MEMBERS,
2652        );
2653
2654        let json = br#"{"known":"yes","Items":[],"extra":true}"#;
2655        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2656        let mut known_val = String::new();
2657        deser
2658            .read_struct(&TEST_SCHEMA, &mut |member, deser| {
2659                if member.member_name() == Some("known") {
2660                    known_val = deser.read_string(dummy_schema())?;
2661                }
2662                Ok(())
2663            })
2664            .unwrap();
2665        assert_eq!(known_val, "yes");
2666    }
2667
2668    #[test]
2669    fn test_skip_value_nested_objects() {
2670        use aws_smithy_schema::ShapeType;
2671        static D_MEMBER: Schema<'static> = Schema::new_member(
2672            aws_smithy_schema::shape_id!("test", "S"),
2673            ShapeType::String,
2674            "d",
2675            0,
2676        );
2677        static MEMBERS: &[&Schema<'_>] = &[&D_MEMBER];
2678        static TEST_SCHEMA: Schema<'static> = Schema::new_struct(
2679            aws_smithy_schema::shape_id!("test", "S"),
2680            ShapeType::Structure,
2681            MEMBERS,
2682        );
2683
2684        let json = br#"{"a":{"b":[1,2,{"c":3}]},"d":"ok"}"#;
2685        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2686        let mut d_val = String::new();
2687        deser
2688            .read_struct(&TEST_SCHEMA, &mut |member, deser| {
2689                if member.member_name() == Some("d") {
2690                    d_val = deser.read_string(dummy_schema())?;
2691                }
2692                Ok(())
2693            })
2694            .unwrap();
2695        assert_eq!(d_val, "ok");
2696    }
2697
2698    // Regression tests for bugs discovered by fuzzing (see PR #4608).
2699    // These exercise the same pathological inputs outside of the fuzz
2700    // harness so the fixes stay protected even if the fuzz targets are
2701    // removed or regress.
2702
2703    #[test]
2704    fn regression_truncated_list_does_not_infinite_loop() {
2705        // Bug 1: `read_list` used to call the consumer on empty input
2706        // (because `first()` returned `None`, not `Some(&b']')`) and
2707        // loop forever because the position never advanced.
2708        let mut deser = JsonDeserializer::new(b"[", Arc::new(JsonCodecSettings::default()));
2709        let err = deser
2710            .read_list(dummy_schema(), &mut |d| {
2711                d.read_integer(dummy_schema()).map(|_| ())
2712            })
2713            .expect_err("truncated list input must be rejected");
2714        assert!(
2715            matches!(err, SerdeError::InvalidInput { .. }),
2716            "expected InvalidInput, got {err:?}"
2717        );
2718    }
2719
2720    #[test]
2721    fn regression_truncated_string_list_does_not_infinite_loop() {
2722        // Same bug class as above, but for the specialized
2723        // `read_string_list` helper.
2724        let mut deser = JsonDeserializer::new(b"[", Arc::new(JsonCodecSettings::default()));
2725        let err = deser
2726            .read_string_list(dummy_schema())
2727            .expect_err("truncated string list input must be rejected");
2728        assert!(
2729            matches!(err, SerdeError::InvalidInput { .. }),
2730            "expected InvalidInput, got {err:?}"
2731        );
2732    }
2733
2734    #[test]
2735    fn regression_truncated_document_array_does_not_infinite_loop() {
2736        // Array branch of `read_document` had the same loop bug.
2737        let mut deser = JsonDeserializer::new(b"[", Arc::new(JsonCodecSettings::default()));
2738        let err = deser
2739            .read_document(dummy_schema())
2740            .expect_err("truncated document array must be rejected");
2741        assert!(
2742            matches!(err, SerdeError::InvalidInput { .. }),
2743            "expected InvalidInput, got {err:?}"
2744        );
2745    }
2746
2747    #[test]
2748    fn regression_unterminated_object_key_does_not_panic() {
2749        // Bug 2: `parse_key` used to advance past the end of the input
2750        // when the closing quote was missing, which made the next call
2751        // to `remaining()` panic with an out-of-range slice index.
2752        use aws_smithy_schema::Schema;
2753
2754        static MEMBER: Schema<'static> = Schema::new_member(
2755            aws_smithy_schema::shape_id!("test", "S"),
2756            aws_smithy_schema::ShapeType::String,
2757            "m",
2758            0,
2759        );
2760        static SCHEMA: Schema<'static> = Schema::new_struct(
2761            aws_smithy_schema::shape_id!("test", "S"),
2762            aws_smithy_schema::ShapeType::Structure,
2763            &[&MEMBER],
2764        );
2765
2766        let mut deser = JsonDeserializer::new(br#"{""#, Arc::new(JsonCodecSettings::default()));
2767        let err = deser
2768            .read_struct(&SCHEMA, &mut |_, _| Ok(()))
2769            .expect_err("unterminated object key must be rejected");
2770        assert!(
2771            matches!(err, SerdeError::InvalidInput { .. }),
2772            "expected InvalidInput, got {err:?}"
2773        );
2774    }
2775
2776    #[test]
2777    fn regression_skip_value_truncated_true_does_not_panic() {
2778        // Bug 3: `skip_value` used to blindly `advance_by(4)` on
2779        // `Some(b't')`, running off the end of the buffer when the
2780        // input was shorter than `true`.
2781        use aws_smithy_schema::Schema;
2782
2783        static MEMBER: Schema<'static> = Schema::new_member(
2784            aws_smithy_schema::shape_id!("test", "S"),
2785            aws_smithy_schema::ShapeType::String,
2786            "known",
2787            0,
2788        );
2789        static SCHEMA: Schema<'static> = Schema::new_struct(
2790            aws_smithy_schema::shape_id!("test", "S"),
2791            aws_smithy_schema::ShapeType::Structure,
2792            &[&MEMBER],
2793        );
2794
2795        // `"":t"` — unknown key `""` whose value starts with `t` but
2796        // isn't the literal `true`. This drives `skip_value` into the
2797        // `Some(b't')` arm with fewer than 4 bytes remaining.
2798        let input = br#"{"":t"#;
2799        let mut deser = JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()));
2800        let err = deser
2801            .read_struct(&SCHEMA, &mut |_, _| Ok(()))
2802            .expect_err("truncated `true` literal must be rejected");
2803        assert!(
2804            matches!(err, SerdeError::InvalidInput { .. }),
2805            "expected InvalidInput, got {err:?}"
2806        );
2807    }
2808
2809    #[test]
2810    fn regression_skip_value_rejects_malformed_true_literal() {
2811        // Reviewer follow-up on Bug 3: a length check alone is not
2812        // enough — input like `t!!!` has four bytes but isn't `true`.
2813        // `skip_value` must validate the literal content, not just
2814        // that there are enough bytes to advance past.
2815        use aws_smithy_schema::Schema;
2816
2817        static MEMBER: Schema<'static> = Schema::new_member(
2818            aws_smithy_schema::shape_id!("test", "S"),
2819            aws_smithy_schema::ShapeType::String,
2820            "known",
2821            0,
2822        );
2823        static SCHEMA: Schema<'static> = Schema::new_struct(
2824            aws_smithy_schema::shape_id!("test", "S"),
2825            aws_smithy_schema::ShapeType::Structure,
2826            &[&MEMBER],
2827        );
2828
2829        for bad in [
2830            &br#"{"":t!!!}"#[..],
2831            &br#"{"":f!!!!}"#[..],
2832            &br#"{"":n!!!}"#[..],
2833        ] {
2834            let mut deser = JsonDeserializer::new(bad, Arc::new(JsonCodecSettings::default()));
2835            let result = deser.read_struct(&SCHEMA, &mut |_, _| Ok(()));
2836            assert!(
2837                matches!(result, Err(SerdeError::InvalidInput { .. })),
2838                "expected InvalidInput for malformed input {:?}, got {:?}",
2839                std::str::from_utf8(bad).unwrap_or("<non-utf8>"),
2840                result
2841            );
2842        }
2843    }
2844
2845    #[test]
2846    fn regression_truncated_struct_does_not_infinite_loop() {
2847        // Reviewer follow-up: `read_struct` now uses an explicit `None`
2848        // arm inside the loop, so a truncated `{` input is rejected as
2849        // InvalidInput instead of relying on a downstream check to
2850        // catch it. This mirrors the fix for `read_list`.
2851        use aws_smithy_schema::Schema;
2852
2853        static MEMBER: Schema<'static> = Schema::new_member(
2854            aws_smithy_schema::shape_id!("test", "S"),
2855            aws_smithy_schema::ShapeType::String,
2856            "m",
2857            0,
2858        );
2859        static SCHEMA: Schema<'static> = Schema::new_struct(
2860            aws_smithy_schema::shape_id!("test", "S"),
2861            aws_smithy_schema::ShapeType::Structure,
2862            &[&MEMBER],
2863        );
2864
2865        let mut deser = JsonDeserializer::new(b"{", Arc::new(JsonCodecSettings::default()));
2866        let err = deser
2867            .read_struct(&SCHEMA, &mut |_, _| Ok(()))
2868            .expect_err("truncated struct input must be rejected");
2869        assert!(
2870            matches!(err, SerdeError::InvalidInput { .. }),
2871            "expected InvalidInput, got {err:?}"
2872        );
2873    }
2874
2875    #[test]
2876    fn regression_truncated_map_does_not_infinite_loop() {
2877        // Same as above but for `read_map`.
2878        let mut deser = JsonDeserializer::new(b"{", Arc::new(JsonCodecSettings::default()));
2879        let err = deser
2880            .read_map(dummy_schema(), &mut |_, _| Ok(()))
2881            .expect_err("truncated map input must be rejected");
2882        assert!(
2883            matches!(err, SerdeError::InvalidInput { .. }),
2884            "expected InvalidInput, got {err:?}"
2885        );
2886    }
2887
2888    // ---- Recursion-depth guard tests ----
2889    //
2890    // These verify that deeply-nested payloads produce a clean `SerdeError`
2891    // instead of a stack overflow. They exercise the same recursion pattern
2892    // an attacker would: a JSON object/list/map/document nested far past the
2893    // `MAX_DESERIALIZE_DEPTH` limit.
2894
2895    /// Builds `open.repeat(n) + close.repeat(n)`. For lists this is valid JSON
2896    /// (nested empty arrays). For objects this is malformed, but the depth
2897    /// guard fires before the malformed region is reached so reject-tests work
2898    /// either way.
2899    fn build_nested(open: &str, close: &str, n: usize) -> Vec<u8> {
2900        build_nested_with_inner(open, "", close, n)
2901    }
2902
2903    /// Builds `open.repeat(n) + inner + close.repeat(n)` — always valid JSON
2904    /// given a valid `inner`. Used for accept-under-limit tests.
2905    fn build_nested_with_inner(open: &str, inner: &str, close: &str, n: usize) -> Vec<u8> {
2906        let mut out = String::with_capacity(open.len() * n + inner.len() + close.len() * n);
2907        for _ in 0..n {
2908            out.push_str(open);
2909        }
2910        out.push_str(inner);
2911        for _ in 0..n {
2912            out.push_str(close);
2913        }
2914        out.into_bytes()
2915    }
2916
2917    /// A self-referential struct schema: member `"a"` points back to the parent
2918    /// schema. This is the minimum schema needed to exercise the depth guard
2919    /// through `read_struct`'s consumer-callback path (without it, unknown
2920    /// members go through `skip_value` which is iterative).
2921    fn recursive_struct_schema() -> &'static Schema<'static> {
2922        static MEMBER_A: Schema<'static> = Schema::new_member(
2923            aws_smithy_schema::shape_id!("test", "Rec"),
2924            aws_smithy_schema::ShapeType::Structure,
2925            "a",
2926            0,
2927        );
2928        static RECURSIVE: Schema<'static> = Schema::new_struct(
2929            aws_smithy_schema::shape_id!("test", "Rec"),
2930            aws_smithy_schema::ShapeType::Structure,
2931            &[&MEMBER_A],
2932        );
2933        &RECURSIVE
2934    }
2935
2936    /// Consumer that re-enters `read_struct` to exercise the depth guard.
2937    fn recursive_struct_consumer(
2938        _member: &Schema<'_>,
2939        deser: &mut dyn ShapeDeserializer,
2940    ) -> Result<(), SerdeError> {
2941        deser.read_struct(recursive_struct_schema(), &mut recursive_struct_consumer)
2942    }
2943
2944    /// Consumer that re-enters `read_list` to exercise the depth guard.
2945    fn recursive_list_consumer(deser: &mut dyn ShapeDeserializer) -> Result<(), SerdeError> {
2946        deser.read_list(dummy_schema(), &mut recursive_list_consumer)
2947    }
2948
2949    /// Consumer that re-enters `read_map` to exercise the depth guard.
2950    fn recursive_map_consumer(
2951        _key: String,
2952        deser: &mut dyn ShapeDeserializer,
2953    ) -> Result<(), SerdeError> {
2954        deser.read_map(dummy_schema(), &mut recursive_map_consumer)
2955    }
2956
2957    fn assert_depth_error(err: SerdeError) {
2958        match err {
2959            SerdeError::Custom { ref message, .. } => {
2960                assert!(
2961                    message.contains("maximum nesting depth exceeded"),
2962                    "wrong error message: {message}"
2963                );
2964            }
2965            other => panic!("expected Custom depth error, got {other:?}"),
2966        }
2967    }
2968
2969    #[test]
2970    fn depth_limit_read_struct_rejects_deeply_nested() {
2971        // 200 levels — well past the 128 limit. The depth guard fires at
2972        // level 129 before we reach the ending, so the payload doesn't need
2973        // to be valid JSON all the way through.
2974        let payload = build_nested(r#"{"a":"#, "}", 200);
2975        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2976        let err = deser
2977            .read_struct(recursive_struct_schema(), &mut recursive_struct_consumer)
2978            .expect_err("deeply nested struct must be rejected");
2979        assert_depth_error(err);
2980    }
2981
2982    #[test]
2983    fn depth_limit_read_struct_accepts_under_limit() {
2984        // 100 levels of `{"a":` wrapped around an empty `{}` → 101 actual
2985        // `read_struct` calls, which is under the 128 limit.
2986        let payload = build_nested_with_inner(r#"{"a":"#, "{}", "}", 100);
2987        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2988        deser
2989            .read_struct(recursive_struct_schema(), &mut recursive_struct_consumer)
2990            .expect("100-level nesting should succeed");
2991    }
2992
2993    #[test]
2994    fn depth_limit_read_list_rejects_deeply_nested() {
2995        let payload = build_nested("[", "]", 200);
2996        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2997        let err = deser
2998            .read_list(dummy_schema(), &mut recursive_list_consumer)
2999            .expect_err("deeply nested list must be rejected");
3000        assert_depth_error(err);
3001    }
3002
3003    #[test]
3004    fn depth_limit_read_list_accepts_under_limit() {
3005        // `[[[...[]]]]` — 100 nested empty lists is valid JSON on its own.
3006        let payload = build_nested("[", "]", 100);
3007        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
3008        deser
3009            .read_list(dummy_schema(), &mut recursive_list_consumer)
3010            .expect("100-level nesting should succeed");
3011    }
3012
3013    #[test]
3014    fn depth_limit_read_map_rejects_deeply_nested() {
3015        let payload = build_nested(r#"{"k":"#, "}", 200);
3016        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
3017        let err = deser
3018            .read_map(dummy_schema(), &mut recursive_map_consumer)
3019            .expect_err("deeply nested map must be rejected");
3020        assert_depth_error(err);
3021    }
3022
3023    #[test]
3024    fn depth_limit_read_map_accepts_under_limit() {
3025        let payload = build_nested_with_inner(r#"{"k":"#, "{}", "}", 100);
3026        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
3027        deser
3028            .read_map(dummy_schema(), &mut recursive_map_consumer)
3029            .expect("100-level nesting should succeed");
3030    }
3031
3032    #[test]
3033    fn depth_limit_read_document_rejects_deeply_nested_object() {
3034        // `read_document` is directly self-recursive on `{` and `[` branches;
3035        // no external consumer is needed.
3036        let payload = build_nested(r#"{"k":"#, "}", 200);
3037        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
3038        let err = deser
3039            .read_document(dummy_schema())
3040            .expect_err("deeply nested document must be rejected");
3041        assert_depth_error(err);
3042    }
3043
3044    #[test]
3045    fn depth_limit_read_document_rejects_deeply_nested_array() {
3046        let payload = build_nested("[", "]", 200);
3047        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
3048        let err = deser
3049            .read_document(dummy_schema())
3050            .expect_err("deeply nested document array must be rejected");
3051        assert_depth_error(err);
3052    }
3053
3054    #[test]
3055    fn depth_limit_read_document_accepts_under_limit() {
3056        let payload = build_nested_with_inner(r#"{"k":"#, "{}", "}", 100);
3057        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
3058        deser
3059            .read_document(dummy_schema())
3060            .expect("100-level nesting should succeed");
3061    }
3062
3063    #[test]
3064    fn depth_limit_respects_custom_max_depth_setting() {
3065        // A custom, tighter limit trips before the default 128 would.
3066        let settings = JsonCodecSettings::builder().max_depth(16).build();
3067
3068        // 20 levels exceeds the custom limit of 16.
3069        let payload = build_nested("[", "]", 20);
3070        let mut deser = JsonDeserializer::new(&payload, Arc::new(settings));
3071        let err = deser
3072            .read_list(dummy_schema(), &mut recursive_list_consumer)
3073            .expect_err("20-level nesting must exceed custom limit of 16");
3074        assert_depth_error(err);
3075
3076        // 10 levels fits inside the custom limit.
3077        let settings_ok = JsonCodecSettings::builder().max_depth(16).build();
3078        let payload_ok = build_nested("[", "]", 10);
3079        let mut deser_ok = JsonDeserializer::new(&payload_ok, Arc::new(settings_ok));
3080        deser_ok
3081            .read_list(dummy_schema(), &mut recursive_list_consumer)
3082            .expect("10-level nesting should succeed under custom limit");
3083    }
3084
3085    // --- read_document big-number fall-through (SEP "Reporting `Document` ambiguous shape types") ---
3086
3087    #[test]
3088    fn read_document_keeps_in_range_int_as_pos_int() {
3089        // Baseline: a value that fits in `u64` continues to land in
3090        // `Number::PosInt` — no behavior change for the common case.
3091        let mut deser = JsonDeserializer::new(b"42", Arc::new(JsonCodecSettings::default()));
3092        match deser.read_document(dummy_schema()).unwrap() {
3093            Document::Number(Number::PosInt(n)) => assert_eq!(n, 42),
3094            other => panic!("expected Number::PosInt(42), got {other:?}"),
3095        }
3096    }
3097
3098    #[test]
3099    fn read_document_keeps_in_range_negative_int_as_neg_int() {
3100        let mut deser = JsonDeserializer::new(b"-123", Arc::new(JsonCodecSettings::default()));
3101        match deser.read_document(dummy_schema()).unwrap() {
3102            Document::Number(Number::NegInt(n)) => assert_eq!(n, -123),
3103            other => panic!("expected Number::NegInt(-123), got {other:?}"),
3104        }
3105    }
3106
3107    #[test]
3108    fn read_document_keeps_finite_float_as_number_float() {
3109        let mut deser = JsonDeserializer::new(b"1.5", Arc::new(JsonCodecSettings::default()));
3110        match deser.read_document(dummy_schema()).unwrap() {
3111            Document::Number(Number::Float(f)) => assert_eq!(f, 1.5),
3112            other => panic!("expected Number::Float(1.5), got {other:?}"),
3113        }
3114    }
3115
3116    /// Regression (proof 54): exponent signs are part of a document number.
3117    /// The number's extent used to come from a scanner that accepted `-`
3118    /// only at index 0, so `1e-5` was cut to `1e` and rejected.
3119    #[test]
3120    fn read_document_reads_signed_exponents() {
3121        for strict in [false, true] {
3122            let settings = Arc::new(
3123                JsonCodecSettings::builder()
3124                    .enforce_strictness(strict)
3125                    .build(),
3126            );
3127            for (input, expected) in [
3128                ("1e-5", 1e-5),
3129                ("-2e-1", -2e-1),
3130                ("1.5E-3", 1.5e-3),
3131                ("1e-0", 1.0),
3132                ("1e+2", 100.0),
3133                ("1E+2", 100.0),
3134                ("-0.0", -0.0),
3135            ] {
3136                let mut deser = JsonDeserializer::new(input.as_bytes(), settings.clone());
3137                match deser.read_document(dummy_schema()) {
3138                    Ok(Document::Number(Number::Float(f))) => {
3139                        assert_eq!(f, expected, "{input} (strict={strict})");
3140                        assert_eq!(f.is_sign_negative(), expected.is_sign_negative(), "{input}");
3141                    }
3142                    other => panic!("{input} (strict={strict}): expected Float, got {other:?}"),
3143                }
3144            }
3145            // Followed by other tokens: the number ends exactly at its last character.
3146            let mut deser = JsonDeserializer::new(b"[1e-5,-2E+1 ,3]", settings.clone());
3147            assert_eq!(
3148                deser.read_document(dummy_schema()).unwrap(),
3149                Document::Array(vec![
3150                    Document::Number(Number::Float(1e-5)),
3151                    Document::Number(Number::Float(-20.0)),
3152                    Document::Number(Number::PosInt(3)),
3153                ]),
3154                "strict={strict}"
3155            );
3156        }
3157        // Strict mode still rejects malformed numbers.
3158        let strict = Arc::new(
3159            JsonCodecSettings::builder()
3160                .enforce_strictness(true)
3161                .build(),
3162        );
3163        for bad in ["1e", "1e-", "1-2", "01", "1.", "1e+-2", "--1"] {
3164            let mut deser = JsonDeserializer::new(bad.as_bytes(), strict.clone());
3165            assert!(
3166                deser.read_document(dummy_schema()).is_err(),
3167                "strict mode accepted {bad:?}"
3168            );
3169        }
3170    }
3171
3172    #[test]
3173    fn read_document_falls_back_to_float_for_oversize_positive_int() {
3174        // 23-digit integer overflows `u64` (max is ~1.84e19, 20 digits).
3175        // `Document` has only `Number` for numerics, so the value falls
3176        // back to `Number::Float`, matching the released
3177        // `aws_smithy_json::deserialize` token reader. This is lossy —
3178        // arbitrary-precision *shapes* avoid it by going through
3179        // `read_big_integer`, which keeps the source text intact.
3180        let bytes = b"99999999999999999999999";
3181        let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3182        match deser.read_document(dummy_schema()).unwrap() {
3183            Document::Number(Number::Float(f)) => {
3184                assert_eq!(f, 99999999999999999999999f64);
3185            }
3186            other => panic!("expected Number::Float, got {other:?}"),
3187        }
3188    }
3189
3190    #[test]
3191    fn read_document_falls_back_to_float_for_oversize_negative_int() {
3192        // 23-digit negative integer overflows `i64` (min is ~-9.2e18,
3193        // 19 digits). Same fall-through as the positive case.
3194        let bytes = b"-99999999999999999999999";
3195        let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3196        match deser.read_document(dummy_schema()).unwrap() {
3197            Document::Number(Number::Float(f)) => {
3198                assert_eq!(f, -99999999999999999999999f64);
3199            }
3200            other => panic!("expected Number::Float, got {other:?}"),
3201        }
3202    }
3203
3204    #[test]
3205    fn read_document_oversize_decimal_overflows_to_infinity() {
3206        // `1e500` overflows `f64` to `+Infinity`. With only the six
3207        // `Document` variants there is nowhere lossless to put it, so the
3208        // released behavior stands. A `bigDecimal` *shape* is unaffected:
3209        // `read_big_decimal` reads the source text directly.
3210        let bytes = b"1e500";
3211        let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3212        match deser.read_document(dummy_schema()).unwrap() {
3213            Document::Number(Number::Float(f)) => assert!(f.is_infinite() && f.is_sign_positive()),
3214            other => panic!("expected Number::Float(inf), got {other:?}"),
3215        }
3216
3217        // The schema-driven read of the same bytes keeps full precision.
3218        let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3219        let bd = deser.read_big_decimal(dummy_schema()).unwrap();
3220        assert!(
3221            !bd.as_ref().to_lowercase().contains("inf"),
3222            "expected a finite arbitrary-precision repr, got {}",
3223            bd.as_ref()
3224        );
3225    }
3226
3227    #[test]
3228    fn read_document_keeps_max_u64_as_pos_int() {
3229        // `u64::MAX` fits in `u64` exactly. Boundary check that the
3230        // overflow fall-through doesn't fire on values that DO fit.
3231        let bytes = b"18446744073709551615";
3232        let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3233        match deser.read_document(dummy_schema()).unwrap() {
3234            Document::Number(Number::PosInt(n)) => assert_eq!(n, u64::MAX),
3235            other => panic!("expected Number::PosInt(u64::MAX), got {other:?}"),
3236        }
3237    }
3238
3239    #[test]
3240    fn read_document_falls_back_just_over_u64_max() {
3241        // `u64::MAX + 1`. First value that overflows.
3242        let bytes = b"18446744073709551616";
3243        let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3244        match deser.read_document(dummy_schema()).unwrap() {
3245            Document::Number(Number::Float(f)) => assert_eq!(f, 18446744073709551616f64),
3246            other => panic!("expected Number::Float, got {other:?}"),
3247        }
3248    }
3249
3250    // --- read_big_integer / read_big_decimal accept JSON strings (`use_string_for_arbitrary_precision`) ---
3251
3252    #[test]
3253    fn read_big_integer_accepts_json_number() {
3254        let bytes = b"99999999999999999999999";
3255        let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3256        let bi = deser.read_big_integer(dummy_schema()).unwrap();
3257        assert_eq!(bi.as_ref(), "99999999999999999999999");
3258    }
3259
3260    #[test]
3261    fn read_big_integer_accepts_json_string() {
3262        // A sender configured with `use_string_for_arbitrary_precision`
3263        // emits BigIntegers as JSON strings. The receiver must accept
3264        // either form regardless of its own setting — leniency is
3265        // unconditional on the read side.
3266        let bytes = br#""99999999999999999999999""#;
3267        let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3268        let bi = deser.read_big_integer(dummy_schema()).unwrap();
3269        assert_eq!(bi.as_ref(), "99999999999999999999999");
3270    }
3271
3272    #[test]
3273    fn read_big_integer_rejects_other_types() {
3274        // Booleans, arrays, etc. are not valid wire forms for BigInteger.
3275        let bytes = b"true";
3276        let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3277        let err = deser.read_big_integer(dummy_schema()).unwrap_err();
3278        assert!(
3279            matches!(err, SerdeError::TypeMismatch { .. }),
3280            "expected TypeMismatch, got {err:?}"
3281        );
3282    }
3283
3284    #[test]
3285    fn read_big_decimal_accepts_json_number() {
3286        let bytes = b"1.234567890123456789012345";
3287        let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3288        let bd = deser.read_big_decimal(dummy_schema()).unwrap();
3289        assert_eq!(bd.as_ref(), "1.234567890123456789012345");
3290    }
3291
3292    #[test]
3293    fn read_big_decimal_accepts_json_string() {
3294        let bytes = br#""1.234567890123456789012345""#;
3295        let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3296        let bd = deser.read_big_decimal(dummy_schema()).unwrap();
3297        assert_eq!(bd.as_ref(), "1.234567890123456789012345");
3298    }
3299
3300    #[test]
3301    fn read_big_decimal_rejects_other_types() {
3302        let bytes = b"[]";
3303        let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3304        let err = deser.read_big_decimal(dummy_schema()).unwrap_err();
3305        assert!(
3306            matches!(err, SerdeError::TypeMismatch { .. }),
3307            "expected TypeMismatch, got {err:?}"
3308        );
3309    }
3310
3311    #[test]
3312    fn read_big_integer_rejects_malformed_string() {
3313        // A JSON string of non-digits is well-formed JSON but invalid
3314        // BigInteger content. `BigInteger::from_str` should surface
3315        // an `InvalidInput` error.
3316        let bytes = br#""not-a-number""#;
3317        let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3318        let err = deser.read_big_integer(dummy_schema()).unwrap_err();
3319        assert!(
3320            matches!(err, SerdeError::InvalidInput { .. }),
3321            "expected InvalidInput, got {err:?}"
3322        );
3323    }
3324
3325    // --- read_discriminated_document attaches DocumentSettings ----------------------------
3326
3327    #[test]
3328    fn read_discriminated_document_attaches_settings() {
3329        // Settings live on the [`DiscriminatedDocument`] wrapper, not
3330        // on individual data nodes. `read_discriminated_document` is
3331        // the entry point that attaches them.
3332        let mut deser = JsonDeserializer::new(b"\"hello\"", Arc::new(JsonCodecSettings::default()));
3333        let wrapper = deser.read_discriminated_document().unwrap();
3334        assert!(
3335            wrapper.settings().is_some(),
3336            "settings should be attached to the wrapper produced by \
3337             read_discriminated_document"
3338        );
3339    }
3340
3341    #[test]
3342    fn read_discriminated_document_settings_enable_blob_coercion() {
3343        // "aGVsbG8=" is base64("hello"). Without settings, as_blob on a
3344        // String document returns an error. With settings attached by
3345        // `read_discriminated_document`, JsonCodecSettings::coerce_string_to_blob
3346        // decodes it.
3347        let mut deser =
3348            JsonDeserializer::new(br#""aGVsbG8=""#, Arc::new(JsonCodecSettings::default()));
3349        let wrapper = deser.read_discriminated_document().unwrap();
3350        let bytes = wrapper
3351            .as_blob()
3352            .expect("base64 string should decode to blob");
3353        assert_eq!(&*bytes, b"hello");
3354    }
3355
3356    #[test]
3357    fn read_discriminated_document_settings_enable_timestamp_coercion() {
3358        // EpochSeconds is the JSON default; a number value coerces via
3359        // `coerce_number_to_timestamp`.
3360        let mut deser =
3361            JsonDeserializer::new(b"1577836800", Arc::new(JsonCodecSettings::default()));
3362        let wrapper = deser.read_discriminated_document().unwrap();
3363        let ts = wrapper
3364            .as_timestamp()
3365            .expect("epoch-seconds number should decode to timestamp");
3366        assert_eq!(ts.secs(), 1577836800);
3367    }
3368
3369    // -- `__type` discriminator lift -----------------------------------------------------------
3370
3371    #[test]
3372    fn read_discriminated_document_lifts_absolute_type_into_discriminator() {
3373        // An absolute `__type` value lands in the wrapper's
3374        // discriminator slot (as an owned `String` FQN), and the
3375        // `__type` key is dropped from the result map.
3376        let input = br#"{"__type":"smithy.example#Bird","name":"Iago"}"#;
3377        let mut deser = JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()));
3378        let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3379
3380        let id = wrapper
3381            .discriminator()
3382            .expect("absolute __type lifted into discriminator");
3383        assert_eq!(id, "smithy.example#Bird");
3384
3385        let map = wrapper.document().as_object().expect("top-level is map");
3386        assert!(
3387            !map.contains_key("__type"),
3388            "__type must be dropped from result map after lift"
3389        );
3390        assert_eq!(map.get("name").and_then(Document::as_string), Some("Iago"));
3391    }
3392
3393    #[test]
3394    fn read_discriminated_document_relative_type_stays_in_map_when_no_default_namespace() {
3395        // Without a configured `default_namespace` on the codec
3396        // settings, a relative `__type` (no `#`) is left in the map
3397        // as a regular key. The discriminator slot remains unset.
3398        // With a namespace set, see
3399        // `read_discriminated_document_lifts_relative_with_namespace`.
3400        let input = br#"{"__type":"Bird","name":"Iago"}"#;
3401        let mut deser = JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()));
3402        let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3403
3404        assert!(
3405            wrapper.discriminator().is_none(),
3406            "relative __type without default_namespace must not be lifted"
3407        );
3408        let map = wrapper.document().as_object().expect("top-level is map");
3409        assert_eq!(
3410            map.get("__type").and_then(Document::as_string),
3411            Some("Bird")
3412        );
3413    }
3414
3415    #[test]
3416    fn read_discriminated_document_lifts_only_top_level() {
3417        // Only the top-level wrapper has a discriminator slot. A
3418        // `__type` field appearing INSIDE a nested object stays as a
3419        // regular map entry — there's no per-node discriminator to
3420        // lift it into.
3421        let input = br#"{"outer":{"__type":"smithy.example#Inner"}}"#;
3422        let mut deser = JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()));
3423        let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3424
3425        // Outer wrapper has no discriminator (the top-level object
3426        // didn't carry `__type` at its own level).
3427        assert!(wrapper.discriminator().is_none());
3428        // The inner object's `__type` remains as a regular string
3429        // entry in the nested object's map.
3430        let inner = wrapper
3431            .document()
3432            .as_object()
3433            .and_then(|m| m.get("outer"))
3434            .and_then(Document::as_object)
3435            .expect("outer key present and is object");
3436        assert_eq!(
3437            inner.get("__type").and_then(Document::as_string),
3438            Some("smithy.example#Inner"),
3439            "nested __type must remain as a regular map entry: \
3440             only the top-level wrapper has a discriminator slot",
3441        );
3442    }
3443
3444    #[test]
3445    fn read_discriminated_document_malformed_type_stays_in_map() {
3446        // `__type` whose value isn't a well-formed absolute shape ID
3447        // (member-component, multiple `#`, empty parts) stays in the
3448        // map as a regular string entry and discriminator is None.
3449        for malformed in [
3450            "\"smithy.example#Foo$bar\"", // member component not allowed
3451            "\"smithy.example#\"",        // empty shape name
3452            "\"#smithy.example\"",        // empty namespace
3453            "\"no#hash#twice\"",          // multiple `#`
3454        ] {
3455            let body = format!(r#"{{"__type":{malformed}}}"#);
3456            let mut deser =
3457                JsonDeserializer::new(body.as_bytes(), Arc::new(JsonCodecSettings::default()));
3458            let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3459            assert!(
3460                wrapper.discriminator().is_none(),
3461                "malformed __type {malformed} must not be lifted"
3462            );
3463            let map = wrapper.document().as_object().expect("top-level is map");
3464            assert!(
3465                map.contains_key("__type"),
3466                "malformed __type {malformed} must remain in map"
3467            );
3468        }
3469    }
3470
3471    #[test]
3472    fn read_discriminated_document_non_string_type_stays_in_map() {
3473        // `__type` with a non-string value (number, object, array,
3474        // bool, null) stays in the map.
3475        for non_string in [
3476            r#"42"#,
3477            r#"true"#,
3478            r#"null"#,
3479            r#"["a"]"#,
3480            r#"{"nested":true}"#,
3481        ] {
3482            let body = format!(r#"{{"__type":{non_string}}}"#);
3483            let mut deser =
3484                JsonDeserializer::new(body.as_bytes(), Arc::new(JsonCodecSettings::default()));
3485            let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3486            assert!(
3487                wrapper.discriminator().is_none(),
3488                "non-string __type {non_string} must not be lifted"
3489            );
3490            let map = wrapper.document().as_object().expect("top-level is map");
3491            assert!(
3492                map.contains_key("__type"),
3493                "non-string __type {non_string} must remain in map"
3494            );
3495        }
3496    }
3497
3498    // --- read_discriminated_document with default_namespace --------------------------------
3499
3500    fn settings_with_default_namespace(ns: &str) -> Arc<JsonCodecSettings> {
3501        Arc::new(JsonCodecSettings::builder().default_namespace(ns).build())
3502    }
3503
3504    #[test]
3505    fn read_discriminated_document_lifts_relative_with_namespace() {
3506        // With a configured `default_namespace`, a relative `__type`
3507        // is prepended with `<namespace>#` and lifted into the
3508        // discriminator slot. The key is removed from the resulting
3509        // map (matching the absolute-form behavior).
3510        let input = br#"{"__type":"Capacity","CapacityUnits":1.0}"#;
3511        let settings = settings_with_default_namespace("com.amazonaws.dynamodb");
3512        let mut deser = JsonDeserializer::new(input, settings);
3513        let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3514
3515        assert_eq!(
3516            wrapper.discriminator(),
3517            Some("com.amazonaws.dynamodb#Capacity"),
3518            "relative __type must be resolved against default_namespace"
3519        );
3520        let map = wrapper.document().as_object().expect("top-level is map");
3521        assert!(
3522            !map.contains_key("__type"),
3523            "lifted __type must be removed from the map"
3524        );
3525        assert!(
3526            map.contains_key("CapacityUnits"),
3527            "non-discriminator keys remain"
3528        );
3529    }
3530
3531    #[test]
3532    fn read_discriminated_document_absolute_overrides_default_namespace() {
3533        // An absolute `__type` (with `#`) is taken as-is even when a
3534        // default_namespace is set. Different namespaces in __type vs
3535        // settings is a real scenario for cross-namespace discriminators.
3536        let input = br#"{"__type":"smithy.example#Bird","name":"Iago"}"#;
3537        let settings = settings_with_default_namespace("com.amazonaws.dynamodb");
3538        let mut deser = JsonDeserializer::new(input, settings);
3539        let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3540
3541        assert_eq!(
3542            wrapper.discriminator(),
3543            Some("smithy.example#Bird"),
3544            "absolute __type must override default_namespace"
3545        );
3546    }
3547
3548    #[test]
3549    fn read_discriminated_document_invalid_relative_name_stays_in_map() {
3550        // A `__type` value that contains characters illegal in a
3551        // Smithy identifier (e.g. whitespace, punctuation) must not
3552        // be synthesized into a malformed shape ID. The lift is
3553        // skipped and the value stays as a regular map entry.
3554        for invalid in ["foo bar", "foo-bar", "foo.bar", "1leading-digit", ""] {
3555            let body = format!(r#"{{"__type":"{invalid}"}}"#);
3556            let settings = settings_with_default_namespace("com.example");
3557            let mut deser = JsonDeserializer::new(body.as_bytes(), settings);
3558            let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3559            assert!(
3560                wrapper.discriminator().is_none(),
3561                "invalid relative name {invalid:?} must not be lifted"
3562            );
3563            let map = wrapper.document().as_object().expect("top-level is map");
3564            assert!(
3565                map.contains_key("__type"),
3566                "invalid relative name {invalid:?} must remain in map"
3567            );
3568        }
3569    }
3570
3571    #[test]
3572    fn read_discriminated_document_accepts_relative_names_with_underscore() {
3573        // Smithy identifiers allow leading underscore + digits in
3574        // subsequent positions. Cover edge cases.
3575        for valid in ["_Bird", "Bird1", "_a_b_c", "Foo_Bar"] {
3576            let body = format!(r#"{{"__type":"{valid}"}}"#);
3577            let settings = settings_with_default_namespace("ns");
3578            let mut deser = JsonDeserializer::new(body.as_bytes(), settings);
3579            let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3580            assert_eq!(
3581                wrapper.discriminator(),
3582                Some(format!("ns#{valid}").as_str()),
3583                "valid Smithy identifier {valid:?} must be lifted"
3584            );
3585        }
3586    }
3587}
3588
3589/// Deserialization tests for unions, focused on three cases that are easy to
3590/// get wrong in a schema-based deserializer:
3591///
3592/// 1. a union member whose value is **itself a union** (union-in-union),
3593/// 2. an explicitly-null union member (especially the first one), and
3594/// 3. an empty / all-null union object.
3595///
3596/// These mirror the generated union deserializer exactly: a `read_struct`
3597/// over a `ShapeType::Union` schema, a consumer that dispatches on
3598/// `member_index()`, and `ok_or_else(...)` when no variant was set. A
3599/// union-valued member recurses into the inner union's own `deserialize`,
3600/// which performs its own self-contained `read_struct`. Because
3601/// `JsonDeserializer::read_struct` consumes its own object braces and never
3602/// relies on shared "resume" state, the recursion is safe and a null / empty
3603/// union can neither panic nor corrupt the surrounding stream.
3604#[cfg(test)]
3605mod union_deserialization_tests {
3606    use super::*;
3607    use aws_smithy_schema::{shape_id, ShapeType};
3608
3609    // --- union-in-union model ---
3610    //
3611    // Mirrors the real `TargetConfiguration -> mcp -> McpTargetConfiguration`
3612    // shape: an outer union whose `mcp` member targets an inner union whose
3613    // `lambda` member is a string.
3614
3615    static INNER_LAMBDA: Schema<'static> = Schema::new_member(
3616        shape_id!("test", "InnerUnion"),
3617        ShapeType::String,
3618        "lambda",
3619        0,
3620    );
3621    static INNER_UNION_SCHEMA: Schema<'static> = Schema::new_struct(
3622        shape_id!("test", "InnerUnion"),
3623        ShapeType::Union,
3624        &[&INNER_LAMBDA],
3625    );
3626
3627    static OUTER_MCP: Schema<'static> =
3628        Schema::new_member(shape_id!("test", "OuterUnion"), ShapeType::Union, "mcp", 0);
3629    static OUTER_UNION_SCHEMA: Schema<'static> = Schema::new_struct(
3630        shape_id!("test", "OuterUnion"),
3631        ShapeType::Union,
3632        &[&OUTER_MCP],
3633    );
3634
3635    #[derive(Debug, PartialEq)]
3636    enum InnerUnion {
3637        Lambda(String),
3638        Unknown,
3639    }
3640
3641    #[derive(Debug, PartialEq)]
3642    enum OuterUnion {
3643        Mcp(InnerUnion),
3644        Unknown,
3645    }
3646
3647    fn deserialize_inner(deser: &mut dyn ShapeDeserializer) -> Result<InnerUnion, SerdeError> {
3648        let mut result: Option<InnerUnion> = None;
3649        deser.read_struct(&INNER_UNION_SCHEMA, &mut |member, d| {
3650            result = Some(match member.member_index() {
3651                Some(0) => InnerUnion::Lambda(d.read_string(member)?),
3652                _ => InnerUnion::Unknown,
3653            });
3654            Ok(())
3655        })?;
3656        result.ok_or_else(|| SerdeError::custom("expected a union variant"))
3657    }
3658
3659    fn deserialize_outer(deser: &mut dyn ShapeDeserializer) -> Result<OuterUnion, SerdeError> {
3660        let mut result: Option<OuterUnion> = None;
3661        deser.read_struct(&OUTER_UNION_SCHEMA, &mut |member, d| {
3662            result = Some(match member.member_index() {
3663                // The union-valued member recurses into the inner union's own
3664                // deserialize, which does its own self-contained read_struct.
3665                Some(0) => OuterUnion::Mcp(deserialize_inner(d)?),
3666                _ => OuterUnion::Unknown,
3667            });
3668            Ok(())
3669        })?;
3670        result.ok_or_else(|| SerdeError::custom("expected a union variant"))
3671    }
3672
3673    // --- explicit-null / empty / all-null union model ---
3674
3675    static CHOICE_A: Schema<'static> =
3676        Schema::new_member(shape_id!("test", "Choice"), ShapeType::String, "a", 0);
3677    static CHOICE_B: Schema<'static> =
3678        Schema::new_member(shape_id!("test", "Choice"), ShapeType::String, "b", 1);
3679    static CHOICE_SCHEMA: Schema<'static> = Schema::new_struct(
3680        shape_id!("test", "Choice"),
3681        ShapeType::Union,
3682        &[&CHOICE_A, &CHOICE_B],
3683    );
3684
3685    #[derive(Debug, PartialEq)]
3686    enum Choice {
3687        A(String),
3688        B(String),
3689        Unknown,
3690    }
3691
3692    fn deserialize_choice(deser: &mut dyn ShapeDeserializer) -> Result<Choice, SerdeError> {
3693        let mut result: Option<Choice> = None;
3694        deser.read_struct(&CHOICE_SCHEMA, &mut |member, d| {
3695            result = Some(match member.member_index() {
3696                Some(0) => Choice::A(d.read_string(member)?),
3697                Some(1) => Choice::B(d.read_string(member)?),
3698                _ => Choice::Unknown,
3699            });
3700            Ok(())
3701        })?;
3702        result.ok_or_else(|| SerdeError::custom("expected a union variant"))
3703    }
3704
3705    // A struct that holds a union followed by a plain string member. Used to
3706    // prove that reading the union does not corrupt the stream for the
3707    // trailing sibling.
3708    static HOLDER_CHOICE: Schema<'static> =
3709        Schema::new_member(shape_id!("test", "Holder"), ShapeType::Union, "choice", 0);
3710    static HOLDER_TRAILING: Schema<'static> = Schema::new_member(
3711        shape_id!("test", "Holder"),
3712        ShapeType::String,
3713        "trailing",
3714        1,
3715    );
3716    static HOLDER_SCHEMA: Schema<'static> = Schema::new_struct(
3717        shape_id!("test", "Holder"),
3718        ShapeType::Structure,
3719        &[&HOLDER_CHOICE, &HOLDER_TRAILING],
3720    );
3721
3722    fn deser(input: &[u8]) -> JsonDeserializer<'_> {
3723        JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()))
3724    }
3725
3726    #[test]
3727    fn union_member_targeting_a_union_deserializes_without_panic() {
3728        // The exact shape class that crashed the Go SDK: a union member whose
3729        // value is itself a union.
3730        let mut d = deser(br#"{"mcp":{"lambda":"arn:aws:lambda:fn"}}"#);
3731        let got = deserialize_outer(&mut d).expect("union-in-union must deserialize");
3732        assert_eq!(
3733            got,
3734            OuterUnion::Mcp(InnerUnion::Lambda("arn:aws:lambda:fn".to_string()))
3735        );
3736    }
3737
3738    #[test]
3739    fn explicit_null_first_union_member_resolves_later_variant_without_stream_corruption() {
3740        // The first union member is explicitly null; the real variant is the
3741        // second member. read_struct skips the null member and still consumes
3742        // the union's closing brace, so the trailing sibling of the enclosing
3743        // struct parses correctly (i.e. no stream corruption).
3744        let mut choice: Option<Choice> = None;
3745        let mut trailing: Option<String> = None;
3746        let mut d = deser(br#"{"choice":{"a":null,"b":"x"},"trailing":"ok"}"#);
3747        d.read_struct(&HOLDER_SCHEMA, &mut |member, sub| {
3748            match member.member_index() {
3749                Some(0) => choice = Some(deserialize_choice(sub)?),
3750                Some(1) => trailing = Some(sub.read_string(member)?),
3751                _ => {}
3752            }
3753            Ok(())
3754        })
3755        .expect("holder with a null-first-member union must parse");
3756        assert_eq!(choice, Some(Choice::B("x".to_string())));
3757        assert_eq!(
3758            trailing,
3759            Some("ok".to_string()),
3760            "trailing sibling must survive: reading the union must not corrupt the stream"
3761        );
3762    }
3763
3764    #[test]
3765    fn empty_union_object_yields_clean_error_not_panic() {
3766        let mut d = deser(br#"{}"#);
3767        let err = deserialize_choice(&mut d).unwrap_err();
3768        assert!(
3769            err.to_string().contains("expected a union variant"),
3770            "empty union object must be a clean error, got {err:?}"
3771        );
3772    }
3773
3774    #[test]
3775    fn all_null_union_members_yield_clean_error_not_panic() {
3776        let mut d = deser(br#"{"a":null,"b":null}"#);
3777        let err = deserialize_choice(&mut d).unwrap_err();
3778        assert!(
3779            err.to_string().contains("expected a union variant"),
3780            "all-null union must be a clean error, got {err:?}"
3781        );
3782    }
3783
3784    #[test]
3785    fn list_of_union_in_union_deserializes() {
3786        // union-in-union nested inside a list element position.
3787        let mut d = deser(br#"[{"mcp":{"lambda":"a"}},{"mcp":{"lambda":"b"}}]"#);
3788        let mut out = Vec::new();
3789        d.read_list(&aws_smithy_schema::prelude::STRING, &mut |el| {
3790            out.push(deserialize_outer(el)?);
3791            Ok(())
3792        })
3793        .expect("list of union-in-union must deserialize");
3794        assert_eq!(
3795            out,
3796            vec![
3797                OuterUnion::Mcp(InnerUnion::Lambda("a".to_string())),
3798                OuterUnion::Mcp(InnerUnion::Lambda("b".to_string())),
3799            ]
3800        );
3801    }
3802
3803    #[test]
3804    fn map_of_union_in_union_deserializes() {
3805        // union-in-union nested inside a map value position.
3806        let mut d = deser(br#"{"first":{"mcp":{"lambda":"a"}},"second":{"mcp":{"lambda":"b"}}}"#);
3807        let mut out = std::collections::HashMap::new();
3808        d.read_map(&aws_smithy_schema::prelude::STRING, &mut |k, v| {
3809            out.insert(k, deserialize_outer(v)?);
3810            Ok(())
3811        })
3812        .expect("map of union-in-union must deserialize");
3813        assert_eq!(
3814            out.get("first"),
3815            Some(&OuterUnion::Mcp(InnerUnion::Lambda("a".to_string())))
3816        );
3817        assert_eq!(
3818            out.get("second"),
3819            Some(&OuterUnion::Mcp(InnerUnion::Lambda("b".to_string())))
3820        );
3821    }
3822
3823    mod separators {
3824        //! JSON syntax the deserializer must reject: separators (exactly one `,` between
3825        //! elements, none before a closing delimiter), bytes after the top-level value, and
3826        //! malformed values inside skipped unknown members. Smithy protocol tests
3827        //! `RestJsonInvalidJsonBody_case7` (`{"int": 10,}`) and `RestJsonInvalidJsonBody_case1`
3828        //! (`{ "int": 10 }abc`) require the first two.
3829        use super::*;
3830        use aws_smithy_schema::{shape_id, ShapeType};
3831
3832        static INT: Schema<'static> = Schema::new_member(
3833            shape_id!("test", "Input", "int"),
3834            ShapeType::Integer,
3835            "int",
3836            0,
3837        );
3838        static LIST: Schema<'static> = Schema::new_member(
3839            shape_id!("test", "Input", "list"),
3840            ShapeType::List,
3841            "list",
3842            1,
3843        );
3844        static INPUT: Schema<'static> = Schema::new_struct(
3845            shape_id!("test", "Input"),
3846            ShapeType::Structure,
3847            &[&INT, &LIST],
3848        );
3849
3850        fn deser(input: &[u8]) -> JsonDeserializer<'_> {
3851            JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()))
3852        }
3853
3854        /// Reads `INPUT` and returns `(int, list)`; unknown members are skipped.
3855        fn read_input(body: &[u8]) -> Result<(Option<i32>, Vec<i32>), SerdeError> {
3856            let mut d = deser(body);
3857            let mut int = None;
3858            let mut list = Vec::new();
3859            d.read_struct(&INPUT, &mut |member, d| {
3860                match member.member_index() {
3861                    Some(0) => int = Some(d.read_integer(member)?),
3862                    Some(1) => d.read_list(member, &mut |d| {
3863                        list.push(d.read_integer(member)?);
3864                        Ok(())
3865                    })?,
3866                    _ => {}
3867                }
3868                Ok(())
3869            })?;
3870            Ok((int, list))
3871        }
3872
3873        #[test]
3874        fn well_formed_input_is_read() {
3875            assert_eq!(
3876                read_input(br#"{ "int": 10 , "list": [ 1 , 2 ] }"#).unwrap(),
3877                (Some(10), vec![1, 2])
3878            );
3879            assert_eq!(read_input(br#"{}"#).unwrap(), (None, vec![]));
3880            assert_eq!(read_input(br#"{"list":[]}"#).unwrap(), (None, vec![]));
3881            assert_eq!(
3882                read_input(b"  { \"int\": 1 }  \n").unwrap(),
3883                (Some(1), vec![])
3884            );
3885        }
3886
3887        #[test]
3888        fn trailing_comma_in_object_is_rejected() {
3889            assert!(read_input(br#"{"int": 10,}"#).is_err());
3890        }
3891
3892        #[test]
3893        fn leading_and_repeated_commas_in_object_are_rejected() {
3894            assert!(read_input(br#"{,"int": 10}"#).is_err());
3895            assert!(read_input(br#"{"int": 10,,"list": []}"#).is_err());
3896            assert!(read_input(br#"{"int": 10,,}"#).is_err());
3897        }
3898
3899        #[test]
3900        fn missing_comma_in_object_is_rejected() {
3901            assert!(read_input(br#"{"int": 10 "list": []}"#).is_err());
3902        }
3903
3904        #[test]
3905        fn commas_in_array_are_checked() {
3906            assert!(read_input(br#"{"list": [1,]}"#).is_err());
3907            assert!(read_input(br#"{"list": [,1]}"#).is_err());
3908            assert!(read_input(br#"{"list": [1,,2]}"#).is_err());
3909            assert!(read_input(br#"{"list": [1 2]}"#).is_err());
3910        }
3911
3912        #[test]
3913        fn skipped_unknown_members_are_still_validated() {
3914            assert_eq!(
3915                read_input(br#"{"unknown": {"a": [1, {"b": null}], "c": "x"}, "int": 1}"#).unwrap(),
3916                (Some(1), vec![])
3917            );
3918            assert!(read_input(br#"{"unknown": [1,,2], "int": 1}"#).is_err());
3919            assert!(read_input(br#"{"unknown": {"a": 1,}, "int": 1}"#).is_err());
3920            assert!(read_input(br#"{"unknown": "unterminated, "int": 1}"#).is_err());
3921            assert!(read_input(br#"{"unknown": tru, "int": 1}"#).is_err());
3922        }
3923
3924        #[test]
3925        fn trailing_characters_after_the_top_level_value_are_rejected() {
3926            assert!(read_input(br#"{ "int": 10 }abc"#).is_err());
3927            assert!(read_input(br#"{ "int": 10 },"#).is_err());
3928            assert!(read_input(br#"{ "int": 10 }{}"#).is_err());
3929            assert!(deser(br#"[1]x"#).read_integer_list(&LIST).is_err());
3930            assert!(deser(br#"{"k":"v"} {}"#)
3931                .read_string_string_map(&LIST)
3932                .is_err());
3933            assert!(deser(br#"{"a":1}]"#).read_document(&INT).is_err());
3934            assert!(deser(br#"[1,2]"#).read_document(&INT).is_ok());
3935        }
3936
3937        #[test]
3938        fn skipped_numbers_must_match_the_json_grammar() {
3939            for ok in [
3940                b"0" as &[u8],
3941                b"-0",
3942                b"10",
3943                b"-1.5",
3944                b"1e3",
3945                b"1E+3",
3946                b"2.5e-7",
3947            ] {
3948                let body = [b"{\"unknown\": " as &[u8], ok, b", \"int\": 1}"].concat();
3949                assert_eq!(
3950                    read_input(&body).unwrap(),
3951                    (Some(1), vec![]),
3952                    "{}",
3953                    String::from_utf8_lossy(ok)
3954                );
3955            }
3956            for bad in [
3957                b"01" as &[u8],
3958                b"1.",
3959                b"--1",
3960                b"+1",
3961                b".5",
3962                b"1e",
3963                b"1e+",
3964                b"-",
3965                b"0x1",
3966            ] {
3967                let body = [b"{\"unknown\": " as &[u8], bad, b", \"int\": 1}"].concat();
3968                assert!(
3969                    read_input(&body).is_err(),
3970                    "{}",
3971                    String::from_utf8_lossy(bad)
3972                );
3973            }
3974        }
3975
3976        #[test]
3977        fn skipped_strings_must_use_valid_escapes() {
3978            assert_eq!(
3979                read_input(br#"{"unknown": "a\"b\\c\/\b\f\n\r\t\u00e9", "int": 1}"#).unwrap(),
3980                (Some(1), vec![])
3981            );
3982            assert!(read_input(br#"{"unknown": "\q", "int": 1}"#).is_err());
3983            assert!(read_input(br#"{"unknown": "\u12", "int": 1}"#).is_err());
3984            assert!(read_input(br#"{"unknown": "\uZZZZ", "int": 1}"#).is_err());
3985            assert!(read_input(br#"{"unknown": "abc\", "int": 1}"#).is_err());
3986        }
3987
3988        #[test]
3989        fn fast_path_collections_check_separators() {
3990            assert_eq!(
3991                deser(br#"["a","b"]"#).read_string_list(&LIST).unwrap(),
3992                vec!["a", "b"]
3993            );
3994            assert!(deser(br#"["a",]"#).read_string_list(&LIST).is_err());
3995            assert!(deser(br#"[,"a"]"#).read_string_list(&LIST).is_err());
3996            assert!(deser(br#"[1,,2]"#).read_integer_list(&LIST).is_err());
3997            assert!(deser(br#"[1,]"#).read_long_list(&LIST).is_err());
3998            assert!(deser(br#"["YQ==",]"#).read_blob_list(&LIST).is_err());
3999            assert_eq!(
4000                deser(br#"{"k":"v"}"#)
4001                    .read_string_string_map(&LIST)
4002                    .unwrap()
4003                    .len(),
4004                1
4005            );
4006            assert!(deser(br#"{"k":"v",}"#)
4007                .read_string_string_map(&LIST)
4008                .is_err());
4009            assert!(deser(br#"{"k":"v" "j":"w"}"#)
4010                .read_string_string_map(&LIST)
4011                .is_err());
4012        }
4013
4014        #[test]
4015        fn generic_map_checks_separators() {
4016            let mut count = 0;
4017            assert!(deser(br#"{"a":1,"b":2}"#)
4018                .read_map(&LIST, &mut |_, d| {
4019                    count += 1;
4020                    d.read_integer(&INT).map(|_| ())
4021                })
4022                .is_ok());
4023            assert_eq!(count, 2);
4024            assert!(deser(br#"{"a":1,}"#)
4025                .read_map(&LIST, &mut |_, d| d.read_integer(&INT).map(|_| ()))
4026                .is_err());
4027        }
4028
4029        #[test]
4030        fn documents_check_separators() {
4031            assert!(deser(br#"{"a":[1,2],"b":{"c":true}}"#)
4032                .read_document(&INT)
4033                .is_ok());
4034            assert!(deser(br#"{"a":1,}"#).read_document(&INT).is_err());
4035            assert!(deser(br#"[1,,2]"#).read_document(&INT).is_err());
4036            assert!(deser(br#"{,"a":1}"#).read_document(&INT).is_err());
4037        }
4038    }
4039
4040    mod float_strings {
4041        //! A float or double member may carry a string only for a non-finite value
4042        //! (`"NaN"`, `"Infinity"`, `"-Infinity"`). Smithy protocol tests
4043        //! `RestJsonBodyFloatMalformedValueRejected_case0` and
4044        //! `RestJsonBodyDoubleMalformedValueRejected_case0` (`"123"`) require quoted finite
4045        //! numbers to be rejected.
4046        use super::*;
4047        use aws_smithy_schema::{shape_id, ShapeType};
4048
4049        static F: Schema<'static> =
4050            Schema::new_member(shape_id!("test", "Input", "f"), ShapeType::Float, "f", 0);
4051        static D: Schema<'static> =
4052            Schema::new_member(shape_id!("test", "Input", "d"), ShapeType::Double, "d", 1);
4053        static INPUT: Schema<'static> =
4054            Schema::new_struct(shape_id!("test", "Input"), ShapeType::Structure, &[&F, &D]);
4055
4056        fn read(body: &[u8]) -> Result<(Option<f32>, Option<f64>), SerdeError> {
4057            let mut deser = JsonDeserializer::new(body, Arc::new(JsonCodecSettings::default()));
4058            let (mut f, mut d) = (None, None);
4059            deser.read_struct(&INPUT, &mut |m, x| {
4060                match m.member_index() {
4061                    Some(0) => f = Some(x.read_float(m)?),
4062                    Some(1) => d = Some(x.read_double(m)?),
4063                    _ => {}
4064                }
4065                Ok(())
4066            })?;
4067            Ok((f, d))
4068        }
4069
4070        #[test]
4071        fn numbers_and_the_three_special_strings_are_read() {
4072            assert_eq!(
4073                read(br#"{"f": 1.5, "d": -2}"#).unwrap(),
4074                (Some(1.5), Some(-2.0))
4075            );
4076            let (f, d) = read(br#"{"f": "NaN", "d": "-Infinity"}"#).unwrap();
4077            assert!(f.unwrap().is_nan());
4078            assert_eq!(d, Some(f64::NEG_INFINITY));
4079            assert_eq!(
4080                read(br#"{"f": "Infinity"}"#).unwrap().0,
4081                Some(f32::INFINITY)
4082            );
4083        }
4084
4085        #[test]
4086        fn quoted_finite_numbers_are_rejected() {
4087            assert!(read(br#"{"f": "123"}"#).is_err());
4088            assert!(read(br#"{"d": "123"}"#).is_err());
4089            assert!(read(br#"{"d": "1.5e3"}"#).is_err());
4090            assert!(read(br#"{"d": "-0"}"#).is_err());
4091        }
4092
4093        #[test]
4094        fn non_numeric_strings_are_rejected() {
4095            assert!(read(br#"{"d": "abc"}"#).is_err());
4096            assert!(read(br#"{"d": ""}"#).is_err());
4097            assert!(read(br#"{"d": "1 2"}"#).is_err());
4098        }
4099
4100        #[test]
4101        fn alternative_non_finite_spellings_match_the_legacy_parser() {
4102            // `aws_smithy_types::primitive::Parse` falls back to `f64::from_str`, which
4103            // accepts these case-insensitively; only finite results are rejected.
4104            assert!(read(br#"{"f": "nan"}"#).unwrap().0.unwrap().is_nan());
4105            assert_eq!(read(br#"{"d": "inf"}"#).unwrap().1, Some(f64::INFINITY));
4106            assert_eq!(
4107                read(br#"{"d": "-infinity"}"#).unwrap().1,
4108                Some(f64::NEG_INFINITY)
4109            );
4110        }
4111    }
4112
4113    mod timestamp_formats {
4114        //! With `strict_timestamp_formats`, a timestamp must use exactly the wire form its
4115        //! format prescribes, as the restJson1 `MalformedTimestampBody*` protocol tests require.
4116        //! Without it the lenient behavior is unchanged.
4117        use super::*;
4118        use aws_smithy_schema::traits::TimestampFormat;
4119        use aws_smithy_schema::{shape_id, ShapeType};
4120
4121        static DEFAULT: Schema<'static> =
4122            Schema::new_member(shape_id!("test", "T", "d"), ShapeType::Timestamp, "d", 0);
4123        static DATE_TIME: Schema<'static> =
4124            Schema::new_member(shape_id!("test", "T", "dt"), ShapeType::Timestamp, "dt", 1)
4125                .with_timestamp_format(TimestampFormat::DateTime);
4126        static HTTP_DATE: Schema<'static> =
4127            Schema::new_member(shape_id!("test", "T", "hd"), ShapeType::Timestamp, "hd", 2)
4128                .with_timestamp_format(TimestampFormat::HttpDate);
4129        static EPOCH: Schema<'static> =
4130            Schema::new_member(shape_id!("test", "T", "es"), ShapeType::Timestamp, "es", 3)
4131                .with_timestamp_format(TimestampFormat::EpochSeconds);
4132
4133        /// 2018-01-09T20:51:21Z
4134        const T: i64 = 1515531081;
4135
4136        fn read(strict: bool, schema: &Schema<'_>, input: &[u8]) -> Result<DateTime, SerdeError> {
4137            let settings = JsonCodecSettings::builder()
4138                .strict_timestamp_formats(strict)
4139                .build();
4140            JsonDeserializer::new(input, Arc::new(settings)).read_timestamp(schema)
4141        }
4142
4143        fn assert_rejected(schema: &Schema<'_>, inputs: &[&[u8]]) {
4144            for input in inputs {
4145                assert!(
4146                    read(true, schema, input).is_err(),
4147                    "{} must be rejected",
4148                    String::from_utf8_lossy(input)
4149                );
4150            }
4151        }
4152
4153        #[test]
4154        fn strict_epoch_seconds_default_requires_a_number() {
4155            assert_eq!(
4156                read(true, &DEFAULT, b"1515531081").unwrap(),
4157                DateTime::from_secs(T)
4158            );
4159            assert_eq!(
4160                read(true, &DEFAULT, b"1515531081.1234").unwrap(),
4161                DateTime::from_secs_f64(1515531081.1234)
4162            );
4163            assert_rejected(
4164                &DEFAULT,
4165                &[
4166                    br#""1985-04-12T23:20:50.52Z""#,
4167                    br#""1985-04-12T23:20:50Z""#,
4168                    br#""1996-12-19T16:39:57-08:00""#,
4169                    br#""1515531081""#,
4170                    br#""1515531081.1234""#,
4171                    br#""Tue, 29 Apr 2014 18:30:38 GMT""#,
4172                    br#""Infinity""#,
4173                    br#""NaN""#,
4174                    b"true",
4175                ],
4176            );
4177        }
4178
4179        #[test]
4180        fn strict_explicit_epoch_seconds_requires_a_number() {
4181            assert_eq!(
4182                read(true, &EPOCH, b"1515531081").unwrap(),
4183                DateTime::from_secs(T)
4184            );
4185            assert_rejected(&EPOCH, &[br#""2018-01-09T20:51:21Z""#, br#""1515531081""#]);
4186        }
4187
4188        #[test]
4189        fn strict_date_time_requires_an_rfc3339_utc_string() {
4190            assert_eq!(
4191                read(true, &DATE_TIME, br#""2018-01-09T20:51:21Z""#).unwrap(),
4192                DateTime::from_secs(T)
4193            );
4194            assert_rejected(
4195                &DATE_TIME,
4196                &[
4197                    b"1515531081",
4198                    b"1515531081.1234",
4199                    br#""1996-12-19T16:39:57-08:00""#,
4200                    br#""1996-12-19T16:39:57+00""#,
4201                    br#""1996-12-19T16:39:57""#,
4202                    br#""Tue, 29 Apr 2014 18:30:38 GMT""#,
4203                ],
4204            );
4205        }
4206
4207        #[test]
4208        fn strict_http_date_requires_an_imf_fixdate_string() {
4209            assert_eq!(
4210                read(true, &HTTP_DATE, br#""Tue, 09 Jan 2018 20:51:21 GMT""#).unwrap(),
4211                DateTime::from_secs(T)
4212            );
4213            assert_rejected(
4214                &HTTP_DATE,
4215                &[
4216                    b"1515531081",
4217                    b"1515531081.1234",
4218                    br#""1985-04-12T23:20:50.52Z""#,
4219                    br#""1996-12-19T16:39:57-08:00""#,
4220                ],
4221            );
4222        }
4223
4224        #[test]
4225        fn default_is_lenient() {
4226            // A number is epoch seconds whatever the format; a string for a `date-time` or
4227            // `epoch-seconds` member parses as offset-aware `date-time`.
4228            assert_eq!(
4229                read(false, &DATE_TIME, b"1515531081").unwrap(),
4230                DateTime::from_secs(T)
4231            );
4232            assert_eq!(
4233                read(false, &HTTP_DATE, b"1515531081").unwrap(),
4234                DateTime::from_secs(T)
4235            );
4236            assert_eq!(
4237                read(false, &DEFAULT, br#""2018-01-09T20:51:21Z""#).unwrap(),
4238                DateTime::from_secs(T)
4239            );
4240            assert_eq!(
4241                read(false, &DATE_TIME, br#""2018-01-09T21:51:21+01:00""#).unwrap(),
4242                DateTime::from_secs(T)
4243            );
4244            assert_eq!(
4245                read(false, &HTTP_DATE, br#""Tue, 09 Jan 2018 20:51:21 GMT""#).unwrap(),
4246                DateTime::from_secs(T)
4247            );
4248            assert_eq!(
4249                read(false, &EPOCH, br#""1515531081""#).unwrap(),
4250                DateTime::from_secs(T)
4251            );
4252        }
4253    }
4254}
4255
4256/// `read_struct` reports keys that name no member: the consumer is called with the
4257/// prelude `DOCUMENT` schema (no member index) positioned at the value. These tests
4258/// pin down what is reported, what is not, and that the stream stays intact whether
4259/// or not the consumer reads the value.
4260#[cfg(test)]
4261mod unknown_member_tests {
4262    use super::*;
4263    use aws_smithy_schema::{shape_id, ShapeType};
4264
4265    static A: Schema<'static> =
4266        Schema::new_member(shape_id!("test", "S"), ShapeType::String, "a", 0);
4267    static S: Schema<'static> =
4268        Schema::new_struct(shape_id!("test", "S"), ShapeType::Structure, &[&A]);
4269
4270    static U_INT: Schema<'static> =
4271        Schema::new_member(shape_id!("test", "U"), ShapeType::Integer, "int", 0);
4272    static U_STR: Schema<'static> =
4273        Schema::new_member(shape_id!("test", "U"), ShapeType::String, "string", 1);
4274    static U: Schema<'static> =
4275        Schema::new_struct(shape_id!("test", "U"), ShapeType::Union, &[&U_INT, &U_STR]);
4276
4277    fn deser(input: &[u8]) -> JsonDeserializer<'_> {
4278        JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()))
4279    }
4280
4281    #[test]
4282    fn skipped_string_encoding_and_escape_validation_are_independent() {
4283        let settings = Arc::new(
4284            JsonCodecSettings::builder()
4285                .enforce_strictness(true)
4286                .validate_skipped_values(false)
4287                .validate_skipped_string_encoding(true)
4288                .build(),
4289        );
4290        for (value, valid) in [
4291            (br#""\i""#.as_slice(), true),
4292            (br#""\udee9""#, true),
4293            (b"\"raw\ncontrol\"", false),
4294            (b"\"\xff\"", false),
4295        ] {
4296            let input = [b"{\"unknown\":".as_slice(), value, b",\"a\":\"ok\"}"].concat();
4297            let result = JsonDeserializer::new(&input, settings.clone()).read_struct(
4298                &S,
4299                &mut |member, d| {
4300                    if member.member_index().is_some() {
4301                        d.read_string(member).map(|_| ())
4302                    } else {
4303                        Ok(())
4304                    }
4305                },
4306            );
4307            assert_eq!(result.is_ok(), valid, "{value:?}");
4308        }
4309    }
4310
4311    #[test]
4312    fn strictness_applies_to_read_values_not_skipped_values() {
4313        let settings = Arc::new(
4314            JsonCodecSettings::builder()
4315                .enforce_strictness(true)
4316                .validate_skipped_values(false)
4317                .build(),
4318        );
4319        for value in [
4320            br#""\udee9""#.as_slice(),
4321            br#""\i""#,
4322            br#""\u12""#,
4323            b"\"raw\ncontrol\"",
4324            b"\"\xff\"",
4325        ] {
4326            // Both automatic unknown-member skipping and explicit skip_value must
4327            // consume exactly one value, including nested object keys and values.
4328            for (prefix, suffix) in [
4329                (b"".as_slice(), b"".as_slice()),
4330                (b"[", b"]"),
4331                (b"{\"nested\":", b"}"),
4332                (b"{", b":null}"),
4333            ] {
4334                let input = [
4335                    b"{\"unknown\":".as_slice(),
4336                    prefix,
4337                    value,
4338                    suffix,
4339                    b",\"a\":\"ok\"}",
4340                ]
4341                .concat();
4342                for explicit_skip in [false, true] {
4343                    let mut a = None;
4344                    JsonDeserializer::new(&input, settings.clone())
4345                        .read_struct(&S, &mut |member, d| {
4346                            if member.member_index() == Some(0) {
4347                                a = Some(d.read_string(member)?);
4348                            } else if explicit_skip {
4349                                d.skip_value()?;
4350                            }
4351                            Ok(())
4352                        })
4353                        .unwrap();
4354                    assert_eq!(a.as_deref(), Some("ok"));
4355                    let validating =
4356                        Arc::new(settings.to_builder().validate_skipped_values(true).build());
4357                    let rejected = JsonDeserializer::new(&input, validating)
4358                        .read_struct(&S, &mut |member, d| {
4359                            if member.member_index() == Some(0) {
4360                                d.read_string(member)?;
4361                            } else if explicit_skip {
4362                                d.skip_value()?;
4363                            }
4364                            Ok(())
4365                        })
4366                        .is_err();
4367                    assert_eq!(rejected, value == br#""\i""# || value == br#""\u12""#);
4368                }
4369            }
4370            // Strictness must still apply when the same value is actually read,
4371            // including a known field following an ignored field.
4372            let input = [b"{\"unknown\":[],\"a\":".as_slice(), value, b"}"].concat();
4373            assert!(JsonDeserializer::new(&input, settings.clone())
4374                .read_struct(&S, &mut |member, d| {
4375                    if member.member_index() == Some(0) {
4376                        d.read_string(member)?;
4377                    }
4378                    Ok(())
4379                })
4380                .is_err());
4381        }
4382    }
4383
4384    #[test]
4385    fn skipping_without_content_validation_retains_boundaries_and_limits() {
4386        let settings = Arc::new(
4387            JsonCodecSettings::builder()
4388                .enforce_strictness(true)
4389                .validate_skipped_values(false)
4390                .max_depth(3)
4391                .build(),
4392        );
4393        for input in [
4394            br#"{"unknown":"unterminated}"#.as_slice(),
4395            br#"{"unknown":"escaped\"}"#,
4396            br#"{"unknown":[1,2}"#,
4397            br#"{"unknown":[1,,2]}"#,
4398            br#"{"unknown":01}"#,
4399            br#"{"unknown":tru}"#,
4400            br#"{"unknown":[[[[]]]]}"#,
4401        ] {
4402            assert!(
4403                JsonDeserializer::new(input, settings.clone())
4404                    .read_struct(&S, &mut |_, _| Ok(()))
4405                    .is_err(),
4406                "{input:?}"
4407            );
4408        }
4409        let input = br#"{"unknown":"\i\"still ignored\\","a":"ok"}"#;
4410        let mut a = None;
4411        JsonDeserializer::new(input, settings)
4412            .read_struct(&S, &mut |member, d| {
4413                if member.member_index() == Some(0) {
4414                    a = Some(d.read_string(member)?);
4415                }
4416                Ok(())
4417            })
4418            .unwrap();
4419        assert_eq!(a.as_deref(), Some("ok"));
4420    }
4421
4422    /// Reads `S`, recording every unknown key (and its value when asked) and returning `a`.
4423    fn read_s(input: &[u8], read_unknown_value: bool) -> (Option<String>, Vec<Option<Document>>) {
4424        let mut a = None;
4425        let mut unknown = Vec::new();
4426        deser(input)
4427            .read_struct(&S, &mut |member, d| {
4428                match member.member_index() {
4429                    Some(0) => a = Some(d.read_string(member)?),
4430                    _ => {
4431                        assert_eq!(member.shape_type(), ShapeType::Document);
4432                        unknown.push(if read_unknown_value {
4433                            Some(d.read_document(member)?)
4434                        } else {
4435                            None
4436                        });
4437                    }
4438                }
4439                Ok(())
4440            })
4441            .unwrap();
4442        (a, unknown)
4443    }
4444
4445    #[test]
4446    fn unknown_key_is_reported_and_the_consumer_can_read_the_value() {
4447        let (a, unknown) = read_s(br#"{"zzz":{"deep":[1,2]},"a":"x"}"#, true);
4448        assert_eq!(a.as_deref(), Some("x"));
4449        assert_eq!(unknown.len(), 1);
4450        match &unknown[0] {
4451            Some(Document::Object(map)) => assert!(map.contains_key("deep")),
4452            other => panic!("expected the object value, got {other:?}"),
4453        }
4454    }
4455
4456    #[test]
4457    fn unknown_key_value_is_skipped_when_the_consumer_does_not_read_it() {
4458        // Nested containers and a `}` inside a string must not confuse the skip.
4459        let (a, unknown) = read_s(
4460            br#"{"zzz":{"deep":[1,{"q":"}"}]},"a":"x","yyy":"tail"}"#,
4461            false,
4462        );
4463        assert_eq!(a.as_deref(), Some("x"));
4464        assert_eq!(unknown.len(), 2);
4465    }
4466
4467    #[test]
4468    fn type_discriminator_is_not_reported() {
4469        let (a, unknown) = read_s(br#"{"__type":"ns#Foo","a":"x"}"#, false);
4470        assert_eq!(a.as_deref(), Some("x"));
4471        assert!(unknown.is_empty(), "`__type` must not be reported");
4472    }
4473
4474    #[test]
4475    fn null_valued_unknown_key_is_not_reported() {
4476        let (a, unknown) = read_s(br#"{"zzz":null,"a":"x"}"#, false);
4477        assert_eq!(a.as_deref(), Some("x"));
4478        assert!(unknown.is_empty(), "a null value is an absent member");
4479    }
4480
4481    #[test]
4482    fn struct_consumer_that_ignores_unknown_keys_is_unaffected() {
4483        let mut a = None;
4484        deser(br#"{"zzz":[1,2,3],"a":"x"}"#)
4485            .read_struct(&S, &mut |member, d| {
4486                if member.member_index() == Some(0) {
4487                    a = Some(d.read_string(member)?);
4488                }
4489                Ok(())
4490            })
4491            .unwrap();
4492        assert_eq!(a.as_deref(), Some("x"));
4493    }
4494
4495    /// The shape of a generated union deserializer: a second key of any kind is
4496    /// "mixed variants", and an unknown key is an error.
4497    fn read_union(input: &[u8]) -> Result<String, SerdeError> {
4498        let mut result: Option<String> = None;
4499        deser(input).read_struct(&U, &mut |member, d| {
4500            if result.is_some() {
4501                return Err(SerdeError::invalid_input(
4502                    "encountered mixed variants in union",
4503                ));
4504            }
4505            result = Some(match member.member_index() {
4506                Some(0) => format!("int={}", d.read_integer(member)?),
4507                Some(1) => format!("string={}", d.read_string(member)?),
4508                _ => return Err(SerdeError::invalid_input("unexpected union variant")),
4509            });
4510            Ok(())
4511        })?;
4512        result.ok_or_else(|| SerdeError::custom("expected a union variant"))
4513    }
4514
4515    #[test]
4516    fn union_consumer_sees_known_and_unknown_keys() {
4517        assert_eq!(read_union(br#"{"int":2}"#).unwrap(), "int=2");
4518        let err = read_union(br#"{"int":2,"string":"three"}"#).unwrap_err();
4519        assert!(err.to_string().contains("mixed variants"), "{err}");
4520        let err = read_union(br#"{"int":2,"unknownField":"three"}"#).unwrap_err();
4521        assert!(err.to_string().contains("mixed variants"), "{err}");
4522        let err = read_union(br#"{"unknownField":"three"}"#).unwrap_err();
4523        assert!(
4524            err.to_string().contains("unexpected union variant"),
4525            "{err}"
4526        );
4527        // `__type` is a discriminator, not a variant.
4528        assert_eq!(
4529            read_union(br#"{"__type":"ns#U","int":2}"#).unwrap(),
4530            "int=2"
4531        );
4532    }
4533}
4534
4535/// Tests for the [`ShapeDeserializer::skip_value`] contract.
4536///
4537/// JSON is cursor-based, so the override must advance past the complete value. The
4538/// scenario these reproduce is the one the HTTP response composite relies on: a body
4539/// carries a field for a member whose authoritative value came from a header or the
4540/// status code, so the consumer declines it and the remaining members must still parse.
4541#[cfg(test)]
4542mod skip_value_contract {
4543    use super::*;
4544    use aws_smithy_schema::Schema;
4545
4546    static BOUND: Schema<'static> = Schema::new_member(
4547        aws_smithy_schema::shape_id!("test", "Output"),
4548        aws_smithy_schema::ShapeType::String,
4549        "bound",
4550        0,
4551    );
4552    static BODY: Schema<'static> = Schema::new_member(
4553        aws_smithy_schema::shape_id!("test", "Output"),
4554        aws_smithy_schema::ShapeType::String,
4555        "body",
4556        1,
4557    );
4558    static OUTPUT: Schema<'static> = Schema::new_struct(
4559        aws_smithy_schema::shape_id!("test", "Output"),
4560        aws_smithy_schema::ShapeType::Structure,
4561        &[&BOUND, &BODY],
4562    );
4563
4564    /// Reads `OUTPUT`, calling `skip_value` for `bound` and reading `body` normally.
4565    /// Returns the value of `body`, proving the parse continued correctly past the
4566    /// skipped member.
4567    fn skip_bound_read_body(json: &[u8]) -> Result<Option<String>, SerdeError> {
4568        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
4569        let mut body = None;
4570        deser.read_struct(&OUTPUT, &mut |member, d| {
4571            match member.member_index() {
4572                Some(0) => d.skip_value()?,
4573                Some(1) => body = Some(d.read_string(member)?),
4574                _ => {}
4575            }
4576            Ok(())
4577        })?;
4578        Ok(body)
4579    }
4580
4581    #[test]
4582    fn skipping_a_known_member_lets_the_rest_of_the_struct_parse() {
4583        // Each case puts a differently-shaped value in the skipped member. The cursor
4584        // must land exactly on the following comma in every one.
4585        for value in [
4586            r#""a string""#,
4587            r#""a string with a \" escape and , comma""#,
4588            "123",
4589            "-4.5e10",
4590            "true",
4591            "false",
4592            "null",
4593            "{}",
4594            "[]",
4595            r#"{"nested":{"deeper":[1,2,{"x":"y"}]}}"#,
4596            r#"[[[["deep"]]]]"#,
4597            r#"[{"a":1},{"b":[true,null]}]"#,
4598            // A nested object containing a key with the same name as the member we go
4599            // on to read: skipping must not surface it to the consumer.
4600            r#"{"body":"decoy"}"#,
4601        ] {
4602            let json = format!(r#"{{"bound":{value},"body":"real"}}"#);
4603            let got = skip_bound_read_body(json.as_bytes())
4604                .unwrap_or_else(|e| panic!("skipping {value} failed: {e}"));
4605            assert_eq!(
4606                got.as_deref(),
4607                Some("real"),
4608                "after skipping {value}, `body` should still be read"
4609            );
4610        }
4611    }
4612
4613    #[test]
4614    fn skipping_the_last_member_closes_the_object() {
4615        let json = br#"{"body":"real","bound":{"nested":[1,2]}}"#;
4616        assert_eq!(skip_bound_read_body(json).unwrap().as_deref(), Some("real"));
4617    }
4618
4619    #[test]
4620    fn a_skipped_value_is_still_validated() {
4621        // Discarding the value must not mean tolerating malformed JSON, matching the
4622        // existing behavior for skipped unknown members.
4623        for bad in [
4624            r#"{"bound":{"unclosed":1,"body":"real"}"#,
4625            r#"{"bound":01,"body":"real"}"#,
4626            r#"{"bound":tru,"body":"real"}"#,
4627            r#"{"bound":"unterminated,"body":"real"}"#,
4628        ] {
4629            assert!(
4630                skip_bound_read_body(bad.as_bytes()).is_err(),
4631                "malformed skipped value should be rejected: {bad}"
4632            );
4633        }
4634    }
4635
4636    #[test]
4637    fn skip_value_via_trait_object_matches_the_inherent_skip() {
4638        // The trait method must be the same validating skip, not the allocating default.
4639        let json = br#"{"a":{"b":[1,2,3]},"tail":1}"#;
4640        let settings = Arc::new(JsonCodecSettings::default());
4641
4642        let mut inherent = JsonDeserializer::new(json, settings.clone());
4643        inherent.advance_by(5); // position at the value of "a"
4644        inherent.skip_json_value().unwrap();
4645        let inherent_pos = inherent.position;
4646
4647        let mut via_trait = JsonDeserializer::new(json, settings);
4648        via_trait.advance_by(5);
4649        let dynamic: &mut dyn ShapeDeserializer = &mut via_trait;
4650        dynamic.skip_value().unwrap();
4651        assert_eq!(via_trait.position, inherent_pos);
4652    }
4653
4654    #[test]
4655    fn skip_value_on_an_unknown_member_does_not_double_skip() {
4656        // `read_struct` guards the unknown-member callback by skipping only when the
4657        // consumer left the position untouched. A consumer that skips explicitly must
4658        // not cause a second skip, which would swallow the following member.
4659        let json = br#"{"unknown":{"x":1},"body":"real"}"#;
4660        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
4661        let mut body = None;
4662        let mut saw_unknown = false;
4663        deser
4664            .read_struct(&OUTPUT, &mut |member, d| {
4665                match member.member_index() {
4666                    Some(1) => body = Some(d.read_string(member)?),
4667                    // The index-less prelude `DOCUMENT` schema: the unknown-key report
4668                    // that union consumers rely on.
4669                    None => {
4670                        saw_unknown = true;
4671                        d.skip_value()?;
4672                    }
4673                    _ => {}
4674                }
4675                Ok(())
4676            })
4677            .unwrap();
4678        assert!(saw_unknown, "unknown key should still be reported");
4679        assert_eq!(
4680            body.as_deref(),
4681            Some("real"),
4682            "`body` must not be swallowed by a second skip"
4683        );
4684    }
4685
4686    #[test]
4687    fn unknown_member_is_still_reported_and_skipped_when_the_consumer_ignores_it() {
4688        // The pre-existing position-unchanged fallback must keep working unchanged.
4689        let json = br#"{"unknown":{"x":[1,2]},"body":"real"}"#;
4690        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
4691        let mut body = None;
4692        deser
4693            .read_struct(&OUTPUT, &mut |member, d| {
4694                if member.member_index() == Some(1) {
4695                    body = Some(d.read_string(member)?);
4696                }
4697                Ok(())
4698            })
4699            .unwrap();
4700        assert_eq!(body.as_deref(), Some("real"));
4701    }
4702}