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;
11use aws_smithy_types::{BigDecimal, BigInteger, Blob, DateTime, Document};
12
13use crate::codec::JsonCodecSettings;
14use crate::deserialize::{json_token_iter, Token};
15
16use std::sync::Arc;
17
18/// Maximum recursion depth for deserialization. Payloads nested deeper than
19/// this will produce a [`SerdeError`] instead of risking a stack overflow.
20/// Matches the default used by `serde_json`.
21pub(crate) const MAX_DESERIALIZE_DEPTH: u32 = 128;
22
23/// JSON deserializer that implements the ShapeDeserializer trait.
24pub struct JsonDeserializer<'a> {
25    input: &'a [u8],
26    position: usize,
27    settings: Arc<JsonCodecSettings>,
28    depth: u32,
29}
30
31impl<'a> JsonDeserializer<'a> {
32    /// Creates a new JSON deserializer with the given settings.
33    pub(crate) fn new(input: &'a [u8], settings: Arc<JsonCodecSettings>) -> Self {
34        Self {
35            input,
36            position: 0,
37            settings,
38            depth: 0,
39        }
40    }
41
42    /// Resolves a JSON field name to a member schema.
43    fn resolve_member<'s>(&self, schema: &'s Schema, field_name: &str) -> Option<&'s Schema> {
44        self.settings.field_to_member(schema, field_name)
45    }
46
47    fn remaining(&self) -> &[u8] {
48        &self.input[self.position..]
49    }
50
51    fn advance_by(&mut self, n: usize) {
52        self.position = (self.position + n).min(self.input.len());
53    }
54
55    /// Parse a JSON quoted string key directly from bytes, advancing past it.
56    /// Assumes the current position is at the opening `"`.
57    /// Returns a borrowed `&str` when no escape sequences are present (common case),
58    /// avoiding a heap allocation per JSON key.
59    fn parse_key(&mut self) -> Result<std::borrow::Cow<'a, str>, SerdeError> {
60        let start = self.position + 1; // skip opening quote
61        self.position += 1;
62        let input = self.input;
63        let remaining = &input[start..];
64        let mut i = 0;
65        let mut has_escapes = false;
66        let mut found_end = false;
67        while i < remaining.len() {
68            match remaining[i] {
69                b'"' => {
70                    found_end = true;
71                    break;
72                }
73                b'\\' => {
74                    has_escapes = true;
75                    i += 2;
76                }
77                _ => i += 1,
78            }
79        }
80        if !found_end {
81            return Err(SerdeError::InvalidInput {
82                message: "unterminated string key".into(),
83            });
84        }
85        self.position = start + i + 1; // advance past key bytes + closing quote
86        let key_bytes = &input[start..start + i];
87        if has_escapes {
88            let raw = std::str::from_utf8(key_bytes).map_err(|e| SerdeError::InvalidInput {
89                message: e.to_string(),
90            })?;
91            Ok(std::borrow::Cow::Owned(
92                crate::escape::unescape_string(raw)
93                    .map_err(|e| SerdeError::InvalidInput {
94                        message: e.to_string(),
95                    })?
96                    .into_owned(),
97            ))
98        } else {
99            Ok(std::borrow::Cow::Borrowed(
100                std::str::from_utf8(key_bytes).map_err(|e| SerdeError::InvalidInput {
101                    message: e.to_string(),
102                })?,
103            ))
104        }
105    }
106}
107
108impl<'a> ShapeDeserializer for JsonDeserializer<'a> {
109    fn read_struct(
110        &mut self,
111        schema: &Schema,
112        consumer: &mut dyn FnMut(&Schema, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
113    ) -> Result<(), SerdeError> {
114        self.depth += 1;
115        if self.depth > self.settings.max_depth() {
116            return Err(SerdeError::custom("maximum nesting depth exceeded"));
117        }
118        // Expect opening brace
119        self.skip_whitespace();
120        if self.remaining().is_empty() {
121            // Treat empty input as an empty object (e.g., empty HTTP response body)
122            self.depth -= 1;
123            return Ok(());
124        }
125        if self.remaining().first() != Some(&b'{') {
126            return Err(SerdeError::TypeMismatch {
127                message: "expected object".into(),
128            });
129        }
130        self.advance_by(1);
131
132        loop {
133            self.skip_whitespace();
134
135            // Check for end of object, error on end of input, otherwise
136            // fall through to parse the next key/value pair.
137            match self.remaining().first() {
138                Some(&b'}') => {
139                    self.advance_by(1);
140                    break;
141                }
142                None => {
143                    return Err(SerdeError::InvalidInput {
144                        message: "unexpected end of input in object".into(),
145                    });
146                }
147                Some(&b'"') => {}
148                Some(_) => {
149                    return Err(SerdeError::InvalidInput {
150                        message: "expected object key".into(),
151                    });
152                }
153            }
154
155            // Parse the key directly from bytes
156            let key_str = self.parse_key()?;
157
158            // Skip whitespace and expect colon
159            self.skip_whitespace();
160            if self.remaining().first() != Some(&b':') {
161                return Err(SerdeError::InvalidInput {
162                    message: "expected colon after key".into(),
163                });
164            }
165            self.advance_by(1);
166            self.skip_whitespace();
167
168            // Process the value — skip nulls (they represent absent optional members)
169            let rem = self.remaining();
170            if rem.starts_with(b"null") && !rem.get(4).is_some_and(|b| b.is_ascii_alphanumeric()) {
171                self.advance_by(4);
172            } else if let Some(member_schema) = self.resolve_member(schema, &key_str) {
173                consumer(member_schema, self)?;
174            } else {
175                self.skip_value()?;
176            }
177        }
178
179        self.depth -= 1;
180        Ok(())
181    }
182
183    fn read_list(
184        &mut self,
185        _schema: &Schema,
186        consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
187    ) -> Result<(), SerdeError> {
188        self.depth += 1;
189        if self.depth > self.settings.max_depth() {
190            return Err(SerdeError::custom("maximum nesting depth exceeded"));
191        }
192        self.skip_whitespace();
193        if self.remaining().first() != Some(&b'[') {
194            return Err(SerdeError::TypeMismatch {
195                message: "expected array".into(),
196            });
197        }
198        self.advance_by(1);
199
200        loop {
201            self.skip_whitespace();
202            match self.remaining().first() {
203                Some(&b']') => {
204                    self.advance_by(1);
205                    break;
206                }
207                None => {
208                    return Err(SerdeError::InvalidInput {
209                        message: "unexpected end of input in array".into(),
210                    });
211                }
212                _ => consumer(self)?,
213            }
214        }
215
216        self.depth -= 1;
217        Ok(())
218    }
219
220    fn read_map(
221        &mut self,
222        _schema: &Schema,
223        consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
224    ) -> Result<(), SerdeError> {
225        self.depth += 1;
226        if self.depth > self.settings.max_depth() {
227            return Err(SerdeError::custom("maximum nesting depth exceeded"));
228        }
229        self.skip_whitespace();
230        if self.remaining().first() != Some(&b'{') {
231            return Err(SerdeError::TypeMismatch {
232                message: "expected object".into(),
233            });
234        }
235        self.advance_by(1);
236
237        loop {
238            self.skip_whitespace();
239            match self.remaining().first() {
240                Some(&b'}') => {
241                    self.advance_by(1);
242                    break;
243                }
244                None => {
245                    return Err(SerdeError::InvalidInput {
246                        message: "unexpected end of input in object".into(),
247                    });
248                }
249                Some(&b'"') => {}
250                Some(_) => {
251                    return Err(SerdeError::InvalidInput {
252                        message: "expected key".into(),
253                    });
254                }
255            }
256
257            let key = self.parse_key()?;
258
259            self.skip_whitespace();
260            if self.remaining().first() != Some(&b':') {
261                return Err(SerdeError::InvalidInput {
262                    message: "expected colon".into(),
263                });
264            }
265            self.advance_by(1);
266            self.skip_whitespace();
267
268            consumer(key.into_owned(), self)?;
269        }
270
271        self.depth -= 1;
272        Ok(())
273    }
274
275    fn read_boolean(&mut self, _schema: &Schema) -> Result<bool, SerdeError> {
276        self.skip_whitespace();
277        let rem = self.remaining();
278        if rem.starts_with(b"true") {
279            self.advance_by(4);
280            Ok(true)
281        } else if rem.starts_with(b"false") {
282            self.advance_by(5);
283            Ok(false)
284        } else {
285            Err(SerdeError::TypeMismatch {
286                message: "expected boolean".into(),
287            })
288        }
289    }
290
291    fn read_byte(&mut self, _schema: &Schema) -> Result<i8, SerdeError> {
292        self.read_integer_value().and_then(|n| {
293            i8::try_from(n).map_err(|_| SerdeError::InvalidInput {
294                message: "value out of range for byte".into(),
295            })
296        })
297    }
298
299    fn read_short(&mut self, _schema: &Schema) -> Result<i16, SerdeError> {
300        self.read_integer_value().and_then(|n| {
301            i16::try_from(n).map_err(|_| SerdeError::InvalidInput {
302                message: "value out of range for short".into(),
303            })
304        })
305    }
306
307    fn read_integer(&mut self, _schema: &Schema) -> Result<i32, SerdeError> {
308        self.read_integer_value().and_then(|n| {
309            i32::try_from(n).map_err(|_| SerdeError::InvalidInput {
310                message: "value out of range for integer".into(),
311            })
312        })
313    }
314
315    fn read_long(&mut self, _schema: &Schema) -> Result<i64, SerdeError> {
316        self.read_integer_value()
317    }
318
319    fn read_float(&mut self, _schema: &Schema) -> Result<f32, SerdeError> {
320        self.read_float_value().map(|f| f as f32)
321    }
322
323    fn read_double(&mut self, _schema: &Schema) -> Result<f64, SerdeError> {
324        self.read_float_value()
325    }
326
327    fn read_big_integer(&mut self, _schema: &Schema) -> Result<BigInteger, SerdeError> {
328        use std::str::FromStr;
329        self.skip_whitespace();
330        match self.remaining().first() {
331            Some(b'-') | Some(b'0'..=b'9') => {
332                let start = self.position;
333                self.consume_number();
334                let num_str =
335                    std::str::from_utf8(&self.input[start..self.position]).map_err(|e| {
336                        SerdeError::InvalidInput {
337                            message: e.to_string(),
338                        }
339                    })?;
340                BigInteger::from_str(num_str).map_err(|e| SerdeError::InvalidInput {
341                    message: e.to_string(),
342                })
343            }
344            _ => Err(SerdeError::TypeMismatch {
345                message: "expected number".into(),
346            }),
347        }
348    }
349
350    fn read_big_decimal(&mut self, _schema: &Schema) -> Result<BigDecimal, SerdeError> {
351        use std::str::FromStr;
352        self.skip_whitespace();
353        match self.remaining().first() {
354            Some(b'-') | Some(b'0'..=b'9') => {
355                let start = self.position;
356                self.consume_number();
357                let num_str =
358                    std::str::from_utf8(&self.input[start..self.position]).map_err(|e| {
359                        SerdeError::InvalidInput {
360                            message: e.to_string(),
361                        }
362                    })?;
363                BigDecimal::from_str(num_str).map_err(|e| SerdeError::InvalidInput {
364                    message: e.to_string(),
365                })
366            }
367            _ => Err(SerdeError::TypeMismatch {
368                message: "expected number".into(),
369            }),
370        }
371    }
372
373    fn read_string(&mut self, _schema: &Schema) -> Result<String, SerdeError> {
374        self.skip_whitespace();
375        let pos = self.position;
376        let input = self.input;
377        let rem = &input[pos..];
378        if rem.first() != Some(&b'"') {
379            return Err(SerdeError::TypeMismatch {
380                message: "expected string".into(),
381            });
382        }
383        // Scan for end of string, tracking whether escapes are present
384        let mut i = 1;
385        let mut has_escape = false;
386        while i < rem.len() {
387            if rem[i] == b'\\' {
388                has_escape = true;
389                i += 2;
390            } else if rem[i] == b'"' {
391                let raw = &input[pos + 1..pos + i];
392                self.position = pos + i + 1;
393                if !has_escape {
394                    return std::str::from_utf8(raw).map(|s| s.to_owned()).map_err(|e| {
395                        SerdeError::InvalidInput {
396                            message: e.to_string(),
397                        }
398                    });
399                }
400                let s = std::str::from_utf8(raw).map_err(|e| SerdeError::InvalidInput {
401                    message: e.to_string(),
402                })?;
403                return crate::deserialize::EscapedStr::new(s)
404                    .to_unescaped()
405                    .map(|s| s.into_owned())
406                    .map_err(|e| SerdeError::InvalidInput {
407                        message: e.to_string(),
408                    });
409            } else {
410                i += 1;
411            }
412        }
413        Err(SerdeError::InvalidInput {
414            message: "unterminated string".into(),
415        })
416    }
417
418    fn read_blob(&mut self, _schema: &Schema) -> Result<Blob, SerdeError> {
419        let s = self.read_string(_schema)?;
420        let decoded =
421            aws_smithy_types::base64::decode(&s).map_err(|e| SerdeError::InvalidInput {
422                message: format!("invalid base64: {}", e),
423            })?;
424        Ok(Blob::new(decoded))
425    }
426
427    fn read_string_list(&mut self, _schema: &Schema) -> Result<Vec<String>, SerdeError> {
428        self.depth += 1;
429        if self.depth > self.settings.max_depth() {
430            return Err(SerdeError::custom("maximum nesting depth exceeded"));
431        }
432        self.skip_whitespace();
433        if self.remaining().first() != Some(&b'[') {
434            return Err(SerdeError::TypeMismatch {
435                message: "expected array".into(),
436            });
437        }
438        self.advance_by(1);
439        let mut out = Vec::new();
440        loop {
441            self.skip_whitespace();
442            match self.remaining().first() {
443                Some(&b']') => {
444                    self.advance_by(1);
445                    break;
446                }
447                None => {
448                    return Err(SerdeError::InvalidInput {
449                        message: "unexpected end of input in array".into(),
450                    })
451                }
452                _ => out.push(self.read_string(_schema)?),
453            }
454        }
455        self.depth -= 1;
456        Ok(out)
457    }
458
459    fn read_blob_list(&mut self, _schema: &Schema) -> Result<Vec<Blob>, SerdeError> {
460        self.depth += 1;
461        if self.depth > self.settings.max_depth() {
462            return Err(SerdeError::custom("maximum nesting depth exceeded"));
463        }
464        self.skip_whitespace();
465        if self.remaining().first() != Some(&b'[') {
466            return Err(SerdeError::TypeMismatch {
467                message: "expected array".into(),
468            });
469        }
470        self.advance_by(1);
471        let mut out = Vec::new();
472        loop {
473            self.skip_whitespace();
474            match self.remaining().first() {
475                Some(&b']') => {
476                    self.advance_by(1);
477                    break;
478                }
479                None => {
480                    return Err(SerdeError::InvalidInput {
481                        message: "unexpected end of input in array".into(),
482                    })
483                }
484                _ => out.push(self.read_blob(_schema)?),
485            }
486        }
487        self.depth -= 1;
488        Ok(out)
489    }
490
491    fn read_integer_list(&mut self, _schema: &Schema) -> Result<Vec<i32>, SerdeError> {
492        self.depth += 1;
493        if self.depth > self.settings.max_depth() {
494            return Err(SerdeError::custom("maximum nesting depth exceeded"));
495        }
496        self.skip_whitespace();
497        if self.remaining().first() != Some(&b'[') {
498            return Err(SerdeError::TypeMismatch {
499                message: "expected array".into(),
500            });
501        }
502        self.advance_by(1);
503        let mut out = Vec::new();
504        loop {
505            self.skip_whitespace();
506            match self.remaining().first() {
507                Some(&b']') => {
508                    self.advance_by(1);
509                    break;
510                }
511                None => {
512                    return Err(SerdeError::InvalidInput {
513                        message: "unexpected end of input in array".into(),
514                    })
515                }
516                _ => out.push(self.read_integer(_schema)?),
517            }
518        }
519        self.depth -= 1;
520        Ok(out)
521    }
522
523    fn read_long_list(&mut self, _schema: &Schema) -> Result<Vec<i64>, SerdeError> {
524        self.depth += 1;
525        if self.depth > self.settings.max_depth() {
526            return Err(SerdeError::custom("maximum nesting depth exceeded"));
527        }
528        self.skip_whitespace();
529        if self.remaining().first() != Some(&b'[') {
530            return Err(SerdeError::TypeMismatch {
531                message: "expected array".into(),
532            });
533        }
534        self.advance_by(1);
535        let mut out = Vec::new();
536        loop {
537            self.skip_whitespace();
538            match self.remaining().first() {
539                Some(&b']') => {
540                    self.advance_by(1);
541                    break;
542                }
543                None => {
544                    return Err(SerdeError::InvalidInput {
545                        message: "unexpected end of input in array".into(),
546                    })
547                }
548                _ => out.push(self.read_long(_schema)?),
549            }
550        }
551        self.depth -= 1;
552        Ok(out)
553    }
554
555    fn read_string_string_map(
556        &mut self,
557        _schema: &Schema,
558    ) -> Result<std::collections::HashMap<String, String>, SerdeError> {
559        self.depth += 1;
560        if self.depth > self.settings.max_depth() {
561            return Err(SerdeError::custom("maximum nesting depth exceeded"));
562        }
563        self.skip_whitespace();
564        if self.remaining().first() != Some(&b'{') {
565            return Err(SerdeError::TypeMismatch {
566                message: "expected object".into(),
567            });
568        }
569        self.advance_by(1);
570        let mut out = std::collections::HashMap::new();
571        loop {
572            self.skip_whitespace();
573            if self.remaining().first() == Some(&b'}') {
574                self.advance_by(1);
575                break;
576            }
577            if self.remaining().first() != Some(&b'"') {
578                return Err(SerdeError::InvalidInput {
579                    message: "expected key".into(),
580                });
581            }
582            let key = self.parse_key()?;
583            self.skip_whitespace();
584            if self.remaining().first() != Some(&b':') {
585                return Err(SerdeError::InvalidInput {
586                    message: "expected colon".into(),
587                });
588            }
589            self.advance_by(1);
590            self.skip_whitespace();
591            let val = self.read_string(_schema)?;
592            out.insert(key.into_owned(), val);
593        }
594        self.depth -= 1;
595        Ok(out)
596    }
597
598    fn read_timestamp(&mut self, schema: &Schema) -> Result<DateTime, SerdeError> {
599        self.skip_whitespace();
600        let rem = self.remaining();
601        match rem.first() {
602            Some(b'"') => {
603                let s = self.read_string(schema)?;
604                // Determine parse format from @timestampFormat trait or default
605                let format = if let Some(ts_trait) = schema.timestamp_format() {
606                    match ts_trait.format() {
607                        aws_smithy_schema::traits::TimestampFormat::HttpDate => {
608                            aws_smithy_types::date_time::Format::HttpDate
609                        }
610                        aws_smithy_schema::traits::TimestampFormat::EpochSeconds => {
611                            aws_smithy_types::date_time::Format::EpochSeconds
612                        }
613                        aws_smithy_schema::traits::TimestampFormat::DateTime => {
614                            aws_smithy_types::date_time::Format::DateTimeWithOffset
615                        }
616                    }
617                } else {
618                    // Default: try date-time with offsets allowed
619                    aws_smithy_types::date_time::Format::DateTimeWithOffset
620                };
621                DateTime::from_str(&s, format)
622                    .map_err(|e| SerdeError::custom(format!("invalid timestamp string: {e}")))
623            }
624            Some(b'-') | Some(b'0'..=b'9') => {
625                // Numeric timestamp — epoch seconds
626                let start = self.position;
627                self.consume_number();
628                let num_str =
629                    std::str::from_utf8(&self.input[start..self.position]).map_err(|e| {
630                        SerdeError::InvalidInput {
631                            message: e.to_string(),
632                        }
633                    })?;
634                if num_str.contains('.') || num_str.contains('e') || num_str.contains('E') {
635                    let f: f64 = num_str.parse().map_err(|e: std::num::ParseFloatError| {
636                        SerdeError::InvalidInput {
637                            message: e.to_string(),
638                        }
639                    })?;
640                    Ok(DateTime::from_secs_f64(f))
641                } else if num_str.starts_with('-') {
642                    let n: i64 = num_str.parse().map_err(|e: std::num::ParseIntError| {
643                        SerdeError::InvalidInput {
644                            message: e.to_string(),
645                        }
646                    })?;
647                    Ok(DateTime::from_secs(n))
648                } else {
649                    let n: u64 = num_str.parse().map_err(|e: std::num::ParseIntError| {
650                        SerdeError::InvalidInput {
651                            message: e.to_string(),
652                        }
653                    })?;
654                    Ok(DateTime::from_secs(n as i64))
655                }
656            }
657            _ => Err(SerdeError::TypeMismatch {
658                message: "expected timestamp".into(),
659            }),
660        }
661    }
662
663    fn read_document(&mut self, _schema: &Schema) -> Result<Document, SerdeError> {
664        self.depth += 1;
665        if self.depth > self.settings.max_depth() {
666            return Err(SerdeError::custom("maximum nesting depth exceeded"));
667        }
668        self.skip_whitespace();
669        let result = match self.remaining().first() {
670            Some(b'"') => Ok(Document::String(self.read_string(_schema)?)),
671            Some(b't') | Some(b'f') => Ok(Document::Bool(self.read_boolean(_schema)?)),
672            Some(b'n') => {
673                if self.remaining().starts_with(b"null") {
674                    self.advance_by(4);
675                    Ok(Document::Null)
676                } else {
677                    Err(SerdeError::InvalidInput {
678                        message: "unexpected token in document".into(),
679                    })
680                }
681            }
682            Some(b'{') => {
683                self.advance_by(1);
684                let mut map = std::collections::HashMap::new();
685                loop {
686                    self.skip_whitespace();
687                    if self.remaining().first() == Some(&b'}') {
688                        self.advance_by(1);
689                        break;
690                    }
691                    if self.remaining().first() != Some(&b'"') {
692                        return Err(SerdeError::InvalidInput {
693                            message: "expected object key in document".into(),
694                        });
695                    }
696                    let key = self.parse_key()?.into_owned();
697                    self.skip_whitespace();
698                    if self.remaining().first() != Some(&b':') {
699                        return Err(SerdeError::InvalidInput {
700                            message: "expected colon in document object".into(),
701                        });
702                    }
703                    self.advance_by(1);
704                    let value = self.read_document(_schema)?;
705                    map.insert(key, value);
706                }
707                Ok(Document::Object(map))
708            }
709            Some(b'[') => {
710                self.advance_by(1);
711                let mut arr = Vec::new();
712                loop {
713                    self.skip_whitespace();
714                    match self.remaining().first() {
715                        Some(&b']') => {
716                            self.advance_by(1);
717                            break;
718                        }
719                        None => {
720                            return Err(SerdeError::InvalidInput {
721                                message: "unexpected end of input in document array".into(),
722                            })
723                        }
724                        _ => arr.push(self.read_document(_schema)?),
725                    }
726                }
727                Ok(Document::Array(arr))
728            }
729            Some(c) if *c == b'-' || c.is_ascii_digit() => {
730                // Parse number — determine if integer or float
731                let rem = self.remaining();
732                let mut len = 0;
733                let mut is_float = false;
734                let mut is_negative = false;
735                for (i, &b) in rem.iter().enumerate() {
736                    if b == b'-' && i == 0 {
737                        is_negative = true;
738                        len += 1;
739                    } else if b.is_ascii_digit() || b == b'+' {
740                        len += 1;
741                    } else if b == b'.' || b == b'e' || b == b'E' {
742                        is_float = true;
743                        len += 1;
744                    } else {
745                        break;
746                    }
747                }
748                let pos = self.position;
749                self.advance_by(len);
750                let s = std::str::from_utf8(&self.input[pos..pos + len]).map_err(|e| {
751                    SerdeError::InvalidInput {
752                        message: e.to_string(),
753                    }
754                })?;
755                if is_float {
756                    let f = s.parse::<f64>().map_err(|e| SerdeError::InvalidInput {
757                        message: e.to_string(),
758                    })?;
759                    Ok(Document::Number(aws_smithy_types::Number::Float(f)))
760                } else if is_negative {
761                    let n = s.parse::<i64>().map_err(|e| SerdeError::InvalidInput {
762                        message: e.to_string(),
763                    })?;
764                    Ok(Document::Number(aws_smithy_types::Number::NegInt(n)))
765                } else {
766                    let n = s.parse::<u64>().map_err(|e| SerdeError::InvalidInput {
767                        message: e.to_string(),
768                    })?;
769                    Ok(Document::Number(aws_smithy_types::Number::PosInt(n)))
770                }
771            }
772            _ => Err(SerdeError::InvalidInput {
773                message: "unexpected token in document".into(),
774            }),
775        };
776        if result.is_ok() {
777            self.depth -= 1;
778        }
779        result
780    }
781
782    fn is_null(&self) -> bool {
783        let remaining = self.remaining();
784        remaining.len() >= 4
785            && &remaining[..4] == b"null"
786            && !remaining.get(4).is_some_and(|b| b.is_ascii_alphanumeric())
787    }
788
789    fn read_null(&mut self) -> Result<(), SerdeError> {
790        self.skip_whitespace();
791        if self.is_null() {
792            self.advance_by(4);
793        }
794        Ok(())
795    }
796
797    fn container_size(&self) -> Option<usize> {
798        let mut iter = json_token_iter(self.remaining());
799        match iter.next()? {
800            Ok(Token::StartArray { .. }) => {
801                let mut count = 0;
802                let mut depth = 1;
803                for token in iter {
804                    match token {
805                        Ok(Token::StartArray { .. }) | Ok(Token::StartObject { .. }) => {
806                            if depth == 1 {
807                                count += 1;
808                            }
809                            depth += 1;
810                        }
811                        Ok(Token::EndArray { .. }) | Ok(Token::EndObject { .. }) => {
812                            depth -= 1;
813                            if depth == 0 {
814                                return Some(count);
815                            }
816                        }
817                        Ok(Token::ValueBool { .. })
818                        | Ok(Token::ValueNull { .. })
819                        | Ok(Token::ValueString { .. })
820                        | Ok(Token::ValueNumber { .. })
821                            if depth == 1 =>
822                        {
823                            count += 1
824                        }
825                        _ => {}
826                    }
827                }
828                None
829            }
830            Ok(Token::StartObject { .. }) => {
831                let mut count = 0;
832                let mut depth = 1;
833                for token in iter {
834                    match token {
835                        Ok(Token::StartArray { .. }) | Ok(Token::StartObject { .. }) => depth += 1,
836                        Ok(Token::EndArray { .. }) | Ok(Token::EndObject { .. }) => {
837                            depth -= 1;
838                            if depth == 0 {
839                                return Some(count);
840                            }
841                        }
842                        Ok(Token::ObjectKey { .. }) if depth == 1 => count += 1,
843                        _ => {}
844                    }
845                }
846                None
847            }
848            _ => None,
849        }
850    }
851}
852
853impl<'a> JsonDeserializer<'a> {
854    fn skip_whitespace(&mut self) {
855        while self.position < self.input.len() {
856            match self.input[self.position] {
857                b' ' | b'\t' | b'\n' | b'\r' | b',' => self.position += 1,
858                _ => break,
859            }
860        }
861    }
862
863    fn consume_number(&mut self) {
864        let mut len = 0;
865        for &b in self.remaining() {
866            if b.is_ascii_digit() || b == b'-' || b == b'.' || b == b'e' || b == b'E' || b == b'+' {
867                len += 1;
868            } else {
869                break;
870            }
871        }
872        self.advance_by(len);
873    }
874
875    fn skip_value(&mut self) -> Result<(), SerdeError> {
876        self.skip_whitespace();
877        let mut depth: usize = 0;
878        loop {
879            self.skip_whitespace();
880            match self.remaining().first().copied() {
881                Some(b'{') | Some(b'[') => {
882                    self.advance_by(1);
883                    depth += 1;
884                }
885                Some(b'}') | Some(b']') => {
886                    if depth == 0 {
887                        return Err(SerdeError::InvalidInput {
888                            message: "unexpected end token".into(),
889                        });
890                    }
891                    self.advance_by(1);
892                    depth -= 1;
893                    if depth == 0 {
894                        return Ok(());
895                    }
896                }
897                Some(b'"') => {
898                    // Skip quoted string (handles escapes)
899                    let mut i = 1;
900                    let rem = self.remaining();
901                    while i < rem.len() {
902                        if rem[i] == b'\\' {
903                            i += 2; // skip escape sequence
904                        } else if rem[i] == b'"' {
905                            i += 1;
906                            break;
907                        } else {
908                            i += 1;
909                        }
910                    }
911                    self.advance_by(i);
912                    // After a string inside an object, skip optional ':'
913                    if depth > 0 {
914                        self.skip_whitespace();
915                        if self.remaining().first() == Some(&b':') {
916                            self.advance_by(1);
917                            continue; // read the value after the colon
918                        }
919                    }
920                    if depth == 0 {
921                        return Ok(());
922                    }
923                }
924                Some(b't') => {
925                    if !self.remaining().starts_with(b"true") {
926                        return Err(SerdeError::InvalidInput {
927                            message: "expected `true`".into(),
928                        });
929                    }
930                    self.advance_by(4);
931                    if depth == 0 {
932                        return Ok(());
933                    }
934                }
935                Some(b'f') => {
936                    if !self.remaining().starts_with(b"false") {
937                        return Err(SerdeError::InvalidInput {
938                            message: "expected `false`".into(),
939                        });
940                    }
941                    self.advance_by(5);
942                    if depth == 0 {
943                        return Ok(());
944                    }
945                }
946                Some(b'n') => {
947                    if !self.remaining().starts_with(b"null") {
948                        return Err(SerdeError::InvalidInput {
949                            message: "expected `null`".into(),
950                        });
951                    }
952                    self.advance_by(4);
953                    if depth == 0 {
954                        return Ok(());
955                    }
956                }
957                Some(c) if c == b'-' || c.is_ascii_digit() => {
958                    self.consume_number();
959                    if depth == 0 {
960                        return Ok(());
961                    }
962                }
963                Some(_) => {
964                    return Err(SerdeError::InvalidInput {
965                        message: "unexpected token in skip_value".into(),
966                    })
967                }
968                None => {
969                    return Err(SerdeError::InvalidInput {
970                        message: "unexpected end of input".into(),
971                    })
972                }
973            }
974        }
975    }
976
977    fn read_integer_value(&mut self) -> Result<i64, SerdeError> {
978        self.skip_whitespace();
979        let rem = self.remaining();
980        let mut len = 0;
981        for &b in rem {
982            if b.is_ascii_digit() || b == b'-' || b == b'+' {
983                len += 1;
984            } else {
985                break;
986            }
987        }
988        if len == 0 {
989            return Err(SerdeError::TypeMismatch {
990                message: "expected integer".into(),
991            });
992        }
993        let s = std::str::from_utf8(&rem[..len]).map_err(|e| SerdeError::InvalidInput {
994            message: e.to_string(),
995        })?;
996        let n = s.parse::<i64>().map_err(|e| SerdeError::InvalidInput {
997            message: e.to_string(),
998        })?;
999        self.advance_by(len);
1000        Ok(n)
1001    }
1002
1003    fn read_float_value(&mut self) -> Result<f64, SerdeError> {
1004        self.skip_whitespace();
1005        let rem = self.remaining();
1006        // Handle string-encoded special float values: "NaN", "Infinity", "-Infinity"
1007        if rem.first() == Some(&b'"') {
1008            let s = self.read_string(&aws_smithy_schema::prelude::STRING)?;
1009            return match s.as_str() {
1010                "NaN" => Ok(f64::NAN),
1011                "Infinity" => Ok(f64::INFINITY),
1012                "-Infinity" => Ok(f64::NEG_INFINITY),
1013                _ => s.parse::<f64>().map_err(|e| SerdeError::InvalidInput {
1014                    message: e.to_string(),
1015                }),
1016            };
1017        }
1018        let mut len = 0;
1019        for &b in rem {
1020            if b.is_ascii_digit() || b == b'-' || b == b'+' || b == b'.' || b == b'e' || b == b'E' {
1021                len += 1;
1022            } else {
1023                break;
1024            }
1025        }
1026        if len == 0 {
1027            return Err(SerdeError::TypeMismatch {
1028                message: "expected number".into(),
1029            });
1030        }
1031        let s = std::str::from_utf8(&rem[..len]).map_err(|e| SerdeError::InvalidInput {
1032            message: e.to_string(),
1033        })?;
1034        let n = s.parse::<f64>().map_err(|e| SerdeError::InvalidInput {
1035            message: e.to_string(),
1036        })?;
1037        self.advance_by(len);
1038        Ok(n)
1039    }
1040}
1041
1042#[cfg(test)]
1043mod tests {
1044    use super::*;
1045
1046    fn dummy_schema() -> &'static aws_smithy_schema::Schema {
1047        &aws_smithy_schema::prelude::STRING
1048    }
1049
1050    #[test]
1051    fn test_read_boolean() {
1052        let mut deser = JsonDeserializer::new(b"true", Arc::new(JsonCodecSettings::default()));
1053        assert!(deser.read_boolean(dummy_schema()).unwrap());
1054
1055        let mut deser = JsonDeserializer::new(b"false", Arc::new(JsonCodecSettings::default()));
1056        assert!(!(deser.read_boolean(dummy_schema()).unwrap()));
1057    }
1058
1059    #[test]
1060    fn test_read_integer() {
1061        let mut deser = JsonDeserializer::new(b"42", Arc::new(JsonCodecSettings::default()));
1062        assert_eq!(deser.read_integer(dummy_schema()).unwrap(), 42);
1063
1064        let mut deser = JsonDeserializer::new(b"-123", Arc::new(JsonCodecSettings::default()));
1065        assert_eq!(deser.read_integer(dummy_schema()).unwrap(), -123);
1066    }
1067
1068    #[test]
1069    fn test_read_long() {
1070        let mut deser = JsonDeserializer::new(
1071            b"9223372036854775807",
1072            Arc::new(JsonCodecSettings::default()),
1073        );
1074        assert_eq!(deser.read_long(dummy_schema()).unwrap(), i64::MAX);
1075    }
1076
1077    #[test]
1078    fn test_read_float() {
1079        let mut deser = JsonDeserializer::new(b"3.15", Arc::new(JsonCodecSettings::default()));
1080        assert!((deser.read_float(dummy_schema()).unwrap() - 3.15).abs() < 0.01);
1081    }
1082
1083    #[test]
1084    fn test_read_double() {
1085        let mut deser = JsonDeserializer::new(b"2.72", Arc::new(JsonCodecSettings::default()));
1086        assert!((deser.read_double(dummy_schema()).unwrap() - 2.72).abs() < 0.001);
1087    }
1088
1089    #[test]
1090    fn test_read_string() {
1091        let mut deser =
1092            JsonDeserializer::new(br#""hello world""#, Arc::new(JsonCodecSettings::default()));
1093        assert_eq!(deser.read_string(dummy_schema()).unwrap(), "hello world");
1094
1095        let mut deser =
1096            JsonDeserializer::new(br#""hello\nworld""#, Arc::new(JsonCodecSettings::default()));
1097        assert_eq!(deser.read_string(dummy_schema()).unwrap(), "hello\nworld");
1098    }
1099
1100    #[test]
1101    fn test_is_null() {
1102        let deser = JsonDeserializer::new(b"null", Arc::new(JsonCodecSettings::default()));
1103        assert!(deser.is_null());
1104
1105        let deser = JsonDeserializer::new(b"42", Arc::new(JsonCodecSettings::default()));
1106        assert!(!deser.is_null());
1107    }
1108
1109    #[test]
1110    fn test_read_byte_range() {
1111        let mut deser = JsonDeserializer::new(b"127", Arc::new(JsonCodecSettings::default()));
1112        assert_eq!(deser.read_byte(dummy_schema()).unwrap(), 127);
1113
1114        let mut deser = JsonDeserializer::new(b"128", Arc::new(JsonCodecSettings::default()));
1115        assert!(deser.read_byte(dummy_schema()).is_err());
1116    }
1117
1118    #[test]
1119    fn test_read_struct() {
1120        use aws_smithy_schema::Schema;
1121
1122        #[derive(Debug, Default, PartialEq)]
1123        struct Person {
1124            first_name: String,
1125            last_name: String,
1126            age: i32,
1127        }
1128
1129        static FIRST_NAME: Schema = Schema::new_member(
1130            aws_smithy_schema::shape_id!("test", "Person"),
1131            aws_smithy_schema::ShapeType::String,
1132            "firstName",
1133            0,
1134        );
1135        static LAST_NAME: Schema = Schema::new_member(
1136            aws_smithy_schema::shape_id!("test", "Person"),
1137            aws_smithy_schema::ShapeType::String,
1138            "lastName",
1139            1,
1140        );
1141        static AGE: Schema = Schema::new_member(
1142            aws_smithy_schema::shape_id!("test", "Person"),
1143            aws_smithy_schema::ShapeType::Integer,
1144            "age",
1145            2,
1146        );
1147        static PERSON_SCHEMA: Schema = Schema::new_struct(
1148            aws_smithy_schema::shape_id!("test", "Person"),
1149            aws_smithy_schema::ShapeType::Structure,
1150            &[&FIRST_NAME, &LAST_NAME, &AGE],
1151        );
1152
1153        fn consume_person(
1154            person: &mut Person,
1155            schema: &Schema,
1156            deser: &mut dyn ShapeDeserializer,
1157        ) -> Result<(), SerdeError> {
1158            match schema.member_name() {
1159                Some("firstName") => person.first_name = deser.read_string(schema)?,
1160                Some("lastName") => person.last_name = deser.read_string(schema)?,
1161                Some("age") => person.age = deser.read_integer(schema)?,
1162                _ => {}
1163            }
1164            Ok(())
1165        }
1166
1167        let json = br#"{"lastName":"Smithy","firstName":"Alice","age":30}"#;
1168        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1169        let mut person = Person::default();
1170        deser
1171            .read_struct(&PERSON_SCHEMA, &mut |member, d| {
1172                consume_person(&mut person, member, d)
1173            })
1174            .unwrap();
1175        assert_eq!(
1176            person,
1177            Person {
1178                first_name: "Alice".to_string(),
1179                last_name: "Smithy".to_string(),
1180                age: 30
1181            }
1182        );
1183
1184        let json =
1185            br#"{"firstName":          "Alice","age":12345678,     "lastName":"\"Smithy\""}"#;
1186        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1187        let mut person = Person::default();
1188        deser
1189            .read_struct(&PERSON_SCHEMA, &mut |member, d| {
1190                consume_person(&mut person, member, d)
1191            })
1192            .unwrap();
1193        assert_eq!(
1194            person,
1195            Person {
1196                first_name: "Alice".to_string(),
1197                last_name: "\"Smithy\"".to_string(),
1198                age: 12345678
1199            }
1200        );
1201    }
1202
1203    #[test]
1204    fn test_read_list() {
1205        let json = b"[1, 2, 3, 4, 5]";
1206        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1207        let capacity = deser.container_size().unwrap_or(0);
1208        let mut result = Vec::with_capacity(capacity);
1209        let allocated_capacity = result.capacity();
1210        deser
1211            .read_list(dummy_schema(), &mut |deser| {
1212                result.push(deser.read_integer(dummy_schema())?);
1213                Ok(())
1214            })
1215            .unwrap();
1216        assert_eq!(result, vec![1, 2, 3, 4, 5]);
1217        // Ensure no more memory was allocated for the container
1218        assert_eq!(result.capacity(), allocated_capacity);
1219
1220        let json = b"[]";
1221        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1222        let capacity = deser.container_size().unwrap_or(0);
1223        let mut result = Vec::<i32>::with_capacity(capacity);
1224        let allocated_capacity = result.capacity();
1225        deser
1226            .read_list(dummy_schema(), &mut |deser| {
1227                result.push(deser.read_integer(dummy_schema())?);
1228                Ok(())
1229            })
1230            .unwrap();
1231        assert_eq!(result, Vec::<i32>::new());
1232        // Ensure no more memory was allocated for the container
1233        assert_eq!(result.capacity(), allocated_capacity);
1234
1235        let json = br#"["hello", "world"]"#;
1236        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1237        let capacity = deser.container_size().unwrap_or(0);
1238        let mut result = Vec::with_capacity(capacity);
1239        let allocated_capacity = result.capacity();
1240        deser
1241            .read_list(dummy_schema(), &mut |deser| {
1242                result.push(deser.read_string(dummy_schema())?);
1243                Ok(())
1244            })
1245            .unwrap();
1246        assert_eq!(result, vec!["hello", "world"]);
1247        // Ensure no more memory was allocated for the container
1248        assert_eq!(result.capacity(), allocated_capacity);
1249    }
1250
1251    #[test]
1252    fn test_container_size() {
1253        let deser =
1254            JsonDeserializer::new(b"[1, 2, 3, 4, 5]", Arc::new(JsonCodecSettings::default()));
1255        assert_eq!(deser.container_size(), Some(5));
1256
1257        let deser = JsonDeserializer::new(b"[]", Arc::new(JsonCodecSettings::default()));
1258        assert_eq!(deser.container_size(), Some(0));
1259
1260        let deser = JsonDeserializer::new(
1261            br#"{"a": 1, "b": 2, "c": 3}"#,
1262            Arc::new(JsonCodecSettings::default()),
1263        );
1264        assert_eq!(deser.container_size(), Some(3));
1265
1266        let deser = JsonDeserializer::new(b"{}", Arc::new(JsonCodecSettings::default()));
1267        assert_eq!(deser.container_size(), Some(0));
1268
1269        let deser = JsonDeserializer::new(
1270            b"[[1, 2], [3, 4], [5, 6]]",
1271            Arc::new(JsonCodecSettings::default()),
1272        );
1273        assert_eq!(deser.container_size(), Some(3));
1274
1275        let deser = JsonDeserializer::new(b"42", Arc::new(JsonCodecSettings::default()));
1276        assert_eq!(deser.container_size(), None);
1277    }
1278
1279    #[test]
1280    fn test_read_map() {
1281        use std::collections::HashMap;
1282
1283        let json = br#"{"a": 1, "b": 2, "c": 3}"#;
1284        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1285        let calculated_capacity = deser.container_size().unwrap_or(0);
1286        let mut result = HashMap::with_capacity(calculated_capacity);
1287        let allocated_capacity = result.capacity();
1288        deser
1289            .read_map(dummy_schema(), &mut |key, deser| {
1290                result.insert(key, deser.read_integer(dummy_schema())?);
1291                Ok(())
1292            })
1293            .unwrap();
1294        assert_eq!(result.len(), 3);
1295        assert_eq!(result.get("a"), Some(&1));
1296        assert_eq!(result.get("b"), Some(&2));
1297        assert_eq!(result.get("c"), Some(&3));
1298        // Ensure no more memory was allocated for the container
1299        assert_eq!(result.capacity(), allocated_capacity);
1300
1301        let json = b"{}";
1302        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1303        let calculated_capacity = deser.container_size().unwrap_or(0);
1304        let mut result = HashMap::<String, i32>::with_capacity(calculated_capacity);
1305        let allocated_capacity = result.capacity();
1306        deser
1307            .read_map(dummy_schema(), &mut |key, deser| {
1308                result.insert(key, deser.read_integer(dummy_schema())?);
1309                Ok(())
1310            })
1311            .unwrap();
1312        assert_eq!(result, HashMap::<String, i32>::new());
1313        // Ensure no more memory was allocated for the container
1314        assert_eq!(result.capacity(), allocated_capacity);
1315
1316        let json = br#"{"name": "Alice", "city": "Seattle"}"#;
1317        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1318        let calculated_capacity = deser.container_size().unwrap_or(0);
1319        let mut result = HashMap::with_capacity(calculated_capacity);
1320        let allocated_capacity = result.capacity();
1321        deser
1322            .read_map(dummy_schema(), &mut |key, deser| {
1323                result.insert(key, deser.read_string(dummy_schema())?);
1324                Ok(())
1325            })
1326            .unwrap();
1327        assert_eq!(result.len(), 2);
1328        assert_eq!(result.get("name"), Some(&"Alice".to_string()));
1329        assert_eq!(result.get("city"), Some(&"Seattle".to_string()));
1330        // Ensure no more memory was allocated for the container
1331        assert_eq!(result.capacity(), allocated_capacity);
1332    }
1333
1334    #[test]
1335    fn test_nested_complex_deserialization() {
1336        use aws_smithy_schema::Schema;
1337        use std::collections::HashMap;
1338
1339        #[derive(Debug, Default, PartialEq)]
1340        struct Address {
1341            street: String,
1342            city: String,
1343            zip: i32,
1344        }
1345        #[derive(Debug, Default, PartialEq)]
1346        struct Company {
1347            name: String,
1348            employees: Vec<String>,
1349            metadata: HashMap<String, i32>,
1350            active: bool,
1351        }
1352        #[derive(Debug, Default, PartialEq)]
1353        struct User {
1354            id: i64,
1355            name: String,
1356            scores: Vec<f64>,
1357            address: Address,
1358            companies: Vec<Company>,
1359            tags: HashMap<String, String>,
1360        }
1361
1362        // Address members & schema
1363        static ADDR_STREET: Schema = Schema::new_member(
1364            aws_smithy_schema::shape_id!("test", "Address"),
1365            aws_smithy_schema::ShapeType::String,
1366            "street",
1367            0,
1368        );
1369        static ADDR_CITY: Schema = Schema::new_member(
1370            aws_smithy_schema::shape_id!("test", "Address"),
1371            aws_smithy_schema::ShapeType::String,
1372            "city",
1373            1,
1374        );
1375        static ADDR_ZIP: Schema = Schema::new_member(
1376            aws_smithy_schema::shape_id!("test", "Address"),
1377            aws_smithy_schema::ShapeType::Integer,
1378            "zip",
1379            2,
1380        );
1381        static ADDRESS_SCHEMA: Schema = Schema::new_struct(
1382            aws_smithy_schema::shape_id!("test", "Address"),
1383            aws_smithy_schema::ShapeType::Structure,
1384            &[&ADDR_STREET, &ADDR_CITY, &ADDR_ZIP],
1385        );
1386
1387        // Company members & schema
1388        static COMP_NAME: Schema = Schema::new_member(
1389            aws_smithy_schema::shape_id!("test", "Company"),
1390            aws_smithy_schema::ShapeType::String,
1391            "name",
1392            0,
1393        );
1394        static COMP_EMPLOYEES: Schema = Schema::new_member(
1395            aws_smithy_schema::shape_id!("test", "Company"),
1396            aws_smithy_schema::ShapeType::List,
1397            "employees",
1398            1,
1399        );
1400        static COMP_METADATA: Schema = Schema::new_member(
1401            aws_smithy_schema::shape_id!("test", "Company"),
1402            aws_smithy_schema::ShapeType::Map,
1403            "metadata",
1404            2,
1405        );
1406        static COMP_ACTIVE: Schema = Schema::new_member(
1407            aws_smithy_schema::shape_id!("test", "Company"),
1408            aws_smithy_schema::ShapeType::Boolean,
1409            "active",
1410            3,
1411        );
1412        static COMPANY_SCHEMA: Schema = Schema::new_struct(
1413            aws_smithy_schema::shape_id!("test", "Company"),
1414            aws_smithy_schema::ShapeType::Structure,
1415            &[&COMP_NAME, &COMP_EMPLOYEES, &COMP_METADATA, &COMP_ACTIVE],
1416        );
1417
1418        // User members & schema
1419        static USER_ID: Schema = Schema::new_member(
1420            aws_smithy_schema::shape_id!("test", "User"),
1421            aws_smithy_schema::ShapeType::Long,
1422            "id",
1423            0,
1424        );
1425        static USER_NAME: Schema = Schema::new_member(
1426            aws_smithy_schema::shape_id!("test", "User"),
1427            aws_smithy_schema::ShapeType::String,
1428            "name",
1429            1,
1430        );
1431        static USER_SCORES: Schema = Schema::new_member(
1432            aws_smithy_schema::shape_id!("test", "User"),
1433            aws_smithy_schema::ShapeType::List,
1434            "scores",
1435            2,
1436        );
1437        static USER_ADDRESS: Schema = Schema::new_member(
1438            aws_smithy_schema::shape_id!("test", "User"),
1439            aws_smithy_schema::ShapeType::Structure,
1440            "address",
1441            3,
1442        );
1443        static USER_COMPANIES: Schema = Schema::new_member(
1444            aws_smithy_schema::shape_id!("test", "User"),
1445            aws_smithy_schema::ShapeType::List,
1446            "companies",
1447            4,
1448        );
1449        static USER_TAGS: Schema = Schema::new_member(
1450            aws_smithy_schema::shape_id!("test", "User"),
1451            aws_smithy_schema::ShapeType::Map,
1452            "tags",
1453            5,
1454        );
1455        static USER_SCHEMA: Schema = Schema::new_struct(
1456            aws_smithy_schema::shape_id!("test", "User"),
1457            aws_smithy_schema::ShapeType::Structure,
1458            &[
1459                &USER_ID,
1460                &USER_NAME,
1461                &USER_SCORES,
1462                &USER_ADDRESS,
1463                &USER_COMPANIES,
1464                &USER_TAGS,
1465            ],
1466        );
1467
1468        fn consume_address(
1469            addr: &mut Address,
1470            schema: &Schema,
1471            deser: &mut dyn ShapeDeserializer,
1472        ) -> Result<(), SerdeError> {
1473            match schema.member_name() {
1474                Some("street") => addr.street = deser.read_string(schema)?,
1475                Some("city") => addr.city = deser.read_string(schema)?,
1476                Some("zip") => addr.zip = deser.read_integer(schema)?,
1477                _ => {}
1478            }
1479            Ok(())
1480        }
1481
1482        fn consume_company(
1483            comp: &mut Company,
1484            schema: &Schema,
1485            deser: &mut dyn ShapeDeserializer,
1486        ) -> Result<(), SerdeError> {
1487            match schema.member_name() {
1488                Some("name") => comp.name = deser.read_string(schema)?,
1489                Some("active") => comp.active = deser.read_boolean(schema)?,
1490                Some("employees") => {
1491                    let mut v = Vec::new();
1492                    deser.read_list(schema, &mut |d| {
1493                        v.push(d.read_string(dummy_schema())?);
1494                        Ok(())
1495                    })?;
1496                    comp.employees = v;
1497                }
1498                Some("metadata") => {
1499                    let mut m = HashMap::new();
1500                    deser.read_map(schema, &mut |k, d| {
1501                        m.insert(k, d.read_integer(dummy_schema())?);
1502                        Ok(())
1503                    })?;
1504                    comp.metadata = m;
1505                }
1506                _ => {}
1507            }
1508            Ok(())
1509        }
1510
1511        fn consume_user(
1512            user: &mut User,
1513            schema: &Schema,
1514            deser: &mut dyn ShapeDeserializer,
1515        ) -> Result<(), SerdeError> {
1516            match schema.member_name() {
1517                Some("id") => user.id = deser.read_long(schema)?,
1518                Some("name") => user.name = deser.read_string(schema)?,
1519                Some("scores") => {
1520                    let mut v = Vec::new();
1521                    deser.read_list(schema, &mut |d| {
1522                        v.push(d.read_double(dummy_schema())?);
1523                        Ok(())
1524                    })?;
1525                    user.scores = v;
1526                }
1527                Some("address") => {
1528                    let mut addr = Address::default();
1529                    deser.read_struct(&ADDRESS_SCHEMA, &mut |member, d| {
1530                        consume_address(&mut addr, member, d)
1531                    })?;
1532                    user.address = addr;
1533                }
1534                Some("companies") => {
1535                    let mut v = Vec::new();
1536                    deser.read_list(schema, &mut |d| {
1537                        let mut comp = Company::default();
1538                        d.read_struct(&COMPANY_SCHEMA, &mut |member, d| {
1539                            consume_company(&mut comp, member, d)
1540                        })?;
1541                        v.push(comp);
1542                        Ok(())
1543                    })?;
1544                    user.companies = v;
1545                }
1546                Some("tags") => {
1547                    let mut m = HashMap::new();
1548                    deser.read_map(schema, &mut |k, d| {
1549                        m.insert(k, d.read_string(dummy_schema())?);
1550                        Ok(())
1551                    })?;
1552                    user.tags = m;
1553                }
1554                _ => {}
1555            }
1556            Ok(())
1557        }
1558
1559        let json = br#"{
1560            "id": 12345,
1561            "name": "John Doe",
1562            "scores": [95.5, 87.3, 92.1],
1563            "address": {
1564                "street": "123 Main St",
1565                "city": "Seattle",
1566                "zip": 98101
1567            },
1568            "companies": [
1569                {
1570                    "name": "TechCorp",
1571                    "employees": ["Alice", "Bob"],
1572                    "metadata": {"founded": 2010, "size": 500},
1573                    "active": true
1574                },
1575                {
1576                    "name": "StartupInc",
1577                    "employees": ["Charlie"],
1578                    "metadata": {"founded": 2020},
1579                    "active": false
1580                }
1581            ],
1582            "tags": {"role": "admin", "level": "senior"}
1583        }"#;
1584
1585        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1586        let mut user = User::default();
1587        deser
1588            .read_struct(&USER_SCHEMA, &mut |member, d| {
1589                consume_user(&mut user, member, d)
1590            })
1591            .unwrap();
1592
1593        assert_eq!(user.id, 12345);
1594        assert_eq!(user.name, "John Doe");
1595        assert_eq!(user.scores, vec![95.5, 87.3, 92.1]);
1596        assert_eq!(user.address.street, "123 Main St");
1597        assert_eq!(user.address.city, "Seattle");
1598        assert_eq!(user.address.zip, 98101);
1599        assert_eq!(user.companies.len(), 2);
1600        assert_eq!(user.companies[0].name, "TechCorp");
1601        assert_eq!(user.companies[0].employees, vec!["Alice", "Bob"]);
1602        assert_eq!(user.companies[0].metadata.get("founded"), Some(&2010));
1603        assert_eq!(user.companies[0].metadata.get("size"), Some(&500));
1604        assert!(user.companies[0].active);
1605        assert_eq!(user.companies[1].name, "StartupInc");
1606        assert_eq!(user.companies[1].employees, vec!["Charlie"]);
1607        assert_eq!(user.companies[1].metadata.get("founded"), Some(&2020));
1608        assert!(!user.companies[1].active);
1609        assert_eq!(user.tags.get("role"), Some(&"admin".to_string()));
1610        assert_eq!(user.tags.get("level"), Some(&"senior".to_string()));
1611    }
1612
1613    #[test]
1614    fn test_json_name_deserialization() {
1615        use aws_smithy_schema::Schema;
1616
1617        static FOO_MEMBER: Schema = Schema::new_member(
1618            aws_smithy_schema::shape_id!("test", "MyStruct"),
1619            aws_smithy_schema::ShapeType::String,
1620            "foo",
1621            0,
1622        );
1623        // "bar" member has @jsonName("Baz")
1624        static BAR_MEMBER: Schema = Schema::new_member(
1625            aws_smithy_schema::shape_id!("test", "MyStruct"),
1626            aws_smithy_schema::ShapeType::Integer,
1627            "bar",
1628            1,
1629        )
1630        .with_json_name("Baz");
1631        static STRUCT_SCHEMA: Schema = Schema::new_struct(
1632            aws_smithy_schema::shape_id!("test", "MyStruct"),
1633            aws_smithy_schema::ShapeType::Structure,
1634            &[&FOO_MEMBER, &BAR_MEMBER],
1635        );
1636
1637        let json = br#"{"foo":"hello","Baz":42}"#;
1638
1639        // With use_json_name=true, "Baz" resolves to the "bar" member
1640        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1641        let (mut foo, mut bar) = (None::<String>, None::<i32>);
1642        deser
1643            .read_struct(&STRUCT_SCHEMA, &mut |member, d| {
1644                match member.member_name() {
1645                    Some("foo") => foo = Some(d.read_string(member)?),
1646                    Some("bar") => bar = Some(d.read_integer(member)?),
1647                    _ => {}
1648                }
1649                Ok(())
1650            })
1651            .unwrap();
1652        assert_eq!(foo.as_deref(), Some("hello"));
1653        assert_eq!(bar, Some(42));
1654
1655        // With use_json_name=false, "Baz" is unknown and gets skipped
1656        let mut deser = JsonDeserializer::new(
1657            json,
1658            Arc::new(JsonCodecSettings::builder().use_json_name(false).build()),
1659        );
1660        let (mut foo, mut bar) = (None::<String>, None::<i32>);
1661        deser
1662            .read_struct(&STRUCT_SCHEMA, &mut |member, d| {
1663                match member.member_name() {
1664                    Some("foo") => foo = Some(d.read_string(member)?),
1665                    Some("bar") => bar = Some(d.read_integer(member)?),
1666                    _ => {}
1667                }
1668                Ok(())
1669            })
1670            .unwrap();
1671        assert_eq!(foo.as_deref(), Some("hello"));
1672        assert_eq!(bar, None); // "Baz" not recognized without jsonName
1673    }
1674
1675    fn timestamp_schema() -> &'static aws_smithy_schema::Schema {
1676        &aws_smithy_schema::prelude::TIMESTAMP
1677    }
1678
1679    #[test]
1680    fn test_read_timestamp_positive_integer() {
1681        let mut deser =
1682            JsonDeserializer::new(b"1700000000", Arc::new(JsonCodecSettings::default()));
1683        let ts = deser.read_timestamp(timestamp_schema()).unwrap();
1684        assert_eq!(ts, DateTime::from_secs(1700000000));
1685    }
1686
1687    #[test]
1688    fn test_read_timestamp_negative_integer() {
1689        let mut deser = JsonDeserializer::new(b"-1000", Arc::new(JsonCodecSettings::default()));
1690        let ts = deser.read_timestamp(timestamp_schema()).unwrap();
1691        assert_eq!(ts, DateTime::from_secs(-1000));
1692    }
1693
1694    #[test]
1695    fn test_read_timestamp_float() {
1696        // This is the format DynamoDB uses: epoch seconds with fractional part
1697        let mut deser =
1698            JsonDeserializer::new(b"1.615218678973E9", Arc::new(JsonCodecSettings::default()));
1699        let ts = deser.read_timestamp(timestamp_schema()).unwrap();
1700        assert_eq!(ts, DateTime::from_secs_f64(1.615218678973E9));
1701    }
1702
1703    #[test]
1704    fn test_read_timestamp_float_simple() {
1705        let mut deser =
1706            JsonDeserializer::new(b"1700000000.5", Arc::new(JsonCodecSettings::default()));
1707        let ts = deser.read_timestamp(timestamp_schema()).unwrap();
1708        assert_eq!(ts, DateTime::from_secs_f64(1700000000.5));
1709    }
1710
1711    #[test]
1712    fn test_read_timestamp_string_datetime() {
1713        let mut deser = JsonDeserializer::new(
1714            br#""2023-11-14T22:13:20Z""#,
1715            Arc::new(JsonCodecSettings::default()),
1716        );
1717        let ts = deser.read_timestamp(timestamp_schema()).unwrap();
1718        assert_eq!(ts, DateTime::from_secs(1700000000));
1719    }
1720
1721    #[test]
1722    fn test_read_timestamp_invalid() {
1723        let mut deser = JsonDeserializer::new(b"true", Arc::new(JsonCodecSettings::default()));
1724        assert!(deser.read_timestamp(timestamp_schema()).is_err());
1725    }
1726
1727    #[test]
1728    fn test_skip_value_empty_array() {
1729        // Regression: skip_value failed on [] because json_token_iter can't parse ']' as a value start
1730        use aws_smithy_schema::ShapeType;
1731        static KNOWN_MEMBER: Schema = Schema::new_member(
1732            aws_smithy_schema::shape_id!("test", "S"),
1733            ShapeType::String,
1734            "known",
1735            0,
1736        );
1737        static MEMBERS: &[&Schema] = &[&KNOWN_MEMBER];
1738        static TEST_SCHEMA: Schema = Schema::new_struct(
1739            aws_smithy_schema::shape_id!("test", "S"),
1740            ShapeType::Structure,
1741            MEMBERS,
1742        );
1743
1744        let json = br#"{"known":"yes","Items":[],"extra":true}"#;
1745        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1746        let mut known_val = String::new();
1747        deser
1748            .read_struct(&TEST_SCHEMA, &mut |member, deser| {
1749                if member.member_name() == Some("known") {
1750                    known_val = deser.read_string(dummy_schema())?;
1751                }
1752                Ok(())
1753            })
1754            .unwrap();
1755        assert_eq!(known_val, "yes");
1756    }
1757
1758    #[test]
1759    fn test_skip_value_nested_objects() {
1760        use aws_smithy_schema::ShapeType;
1761        static D_MEMBER: Schema = Schema::new_member(
1762            aws_smithy_schema::shape_id!("test", "S"),
1763            ShapeType::String,
1764            "d",
1765            0,
1766        );
1767        static MEMBERS: &[&Schema] = &[&D_MEMBER];
1768        static TEST_SCHEMA: Schema = Schema::new_struct(
1769            aws_smithy_schema::shape_id!("test", "S"),
1770            ShapeType::Structure,
1771            MEMBERS,
1772        );
1773
1774        let json = br#"{"a":{"b":[1,2,{"c":3}]},"d":"ok"}"#;
1775        let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1776        let mut d_val = String::new();
1777        deser
1778            .read_struct(&TEST_SCHEMA, &mut |member, deser| {
1779                if member.member_name() == Some("d") {
1780                    d_val = deser.read_string(dummy_schema())?;
1781                }
1782                Ok(())
1783            })
1784            .unwrap();
1785        assert_eq!(d_val, "ok");
1786    }
1787
1788    // Regression tests for bugs discovered by fuzzing (see PR #4608).
1789    // These exercise the same pathological inputs outside of the fuzz
1790    // harness so the fixes stay protected even if the fuzz targets are
1791    // removed or regress.
1792
1793    #[test]
1794    fn regression_truncated_list_does_not_infinite_loop() {
1795        // Bug 1: `read_list` used to call the consumer on empty input
1796        // (because `first()` returned `None`, not `Some(&b']')`) and
1797        // loop forever because the position never advanced.
1798        let mut deser = JsonDeserializer::new(b"[", Arc::new(JsonCodecSettings::default()));
1799        let err = deser
1800            .read_list(dummy_schema(), &mut |d| {
1801                d.read_integer(dummy_schema()).map(|_| ())
1802            })
1803            .expect_err("truncated list input must be rejected");
1804        assert!(
1805            matches!(err, SerdeError::InvalidInput { .. }),
1806            "expected InvalidInput, got {err:?}"
1807        );
1808    }
1809
1810    #[test]
1811    fn regression_truncated_string_list_does_not_infinite_loop() {
1812        // Same bug class as above, but for the specialized
1813        // `read_string_list` helper.
1814        let mut deser = JsonDeserializer::new(b"[", Arc::new(JsonCodecSettings::default()));
1815        let err = deser
1816            .read_string_list(dummy_schema())
1817            .expect_err("truncated string list input must be rejected");
1818        assert!(
1819            matches!(err, SerdeError::InvalidInput { .. }),
1820            "expected InvalidInput, got {err:?}"
1821        );
1822    }
1823
1824    #[test]
1825    fn regression_truncated_document_array_does_not_infinite_loop() {
1826        // Array branch of `read_document` had the same loop bug.
1827        let mut deser = JsonDeserializer::new(b"[", Arc::new(JsonCodecSettings::default()));
1828        let err = deser
1829            .read_document(dummy_schema())
1830            .expect_err("truncated document array must be rejected");
1831        assert!(
1832            matches!(err, SerdeError::InvalidInput { .. }),
1833            "expected InvalidInput, got {err:?}"
1834        );
1835    }
1836
1837    #[test]
1838    fn regression_unterminated_object_key_does_not_panic() {
1839        // Bug 2: `parse_key` used to advance past the end of the input
1840        // when the closing quote was missing, which made the next call
1841        // to `remaining()` panic with an out-of-range slice index.
1842        use aws_smithy_schema::Schema;
1843
1844        static MEMBER: Schema = Schema::new_member(
1845            aws_smithy_schema::shape_id!("test", "S"),
1846            aws_smithy_schema::ShapeType::String,
1847            "m",
1848            0,
1849        );
1850        static SCHEMA: Schema = Schema::new_struct(
1851            aws_smithy_schema::shape_id!("test", "S"),
1852            aws_smithy_schema::ShapeType::Structure,
1853            &[&MEMBER],
1854        );
1855
1856        let mut deser = JsonDeserializer::new(br#"{""#, Arc::new(JsonCodecSettings::default()));
1857        let err = deser
1858            .read_struct(&SCHEMA, &mut |_, _| Ok(()))
1859            .expect_err("unterminated object key must be rejected");
1860        assert!(
1861            matches!(err, SerdeError::InvalidInput { .. }),
1862            "expected InvalidInput, got {err:?}"
1863        );
1864    }
1865
1866    #[test]
1867    fn regression_skip_value_truncated_true_does_not_panic() {
1868        // Bug 3: `skip_value` used to blindly `advance_by(4)` on
1869        // `Some(b't')`, running off the end of the buffer when the
1870        // input was shorter than `true`.
1871        use aws_smithy_schema::Schema;
1872
1873        static MEMBER: Schema = Schema::new_member(
1874            aws_smithy_schema::shape_id!("test", "S"),
1875            aws_smithy_schema::ShapeType::String,
1876            "known",
1877            0,
1878        );
1879        static SCHEMA: Schema = Schema::new_struct(
1880            aws_smithy_schema::shape_id!("test", "S"),
1881            aws_smithy_schema::ShapeType::Structure,
1882            &[&MEMBER],
1883        );
1884
1885        // `"":t"` — unknown key `""` whose value starts with `t` but
1886        // isn't the literal `true`. This drives `skip_value` into the
1887        // `Some(b't')` arm with fewer than 4 bytes remaining.
1888        let input = br#"{"":t"#;
1889        let mut deser = JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()));
1890        let err = deser
1891            .read_struct(&SCHEMA, &mut |_, _| Ok(()))
1892            .expect_err("truncated `true` literal must be rejected");
1893        assert!(
1894            matches!(err, SerdeError::InvalidInput { .. }),
1895            "expected InvalidInput, got {err:?}"
1896        );
1897    }
1898
1899    #[test]
1900    fn regression_skip_value_rejects_malformed_true_literal() {
1901        // Reviewer follow-up on Bug 3: a length check alone is not
1902        // enough — input like `t!!!` has four bytes but isn't `true`.
1903        // `skip_value` must validate the literal content, not just
1904        // that there are enough bytes to advance past.
1905        use aws_smithy_schema::Schema;
1906
1907        static MEMBER: Schema = Schema::new_member(
1908            aws_smithy_schema::shape_id!("test", "S"),
1909            aws_smithy_schema::ShapeType::String,
1910            "known",
1911            0,
1912        );
1913        static SCHEMA: Schema = Schema::new_struct(
1914            aws_smithy_schema::shape_id!("test", "S"),
1915            aws_smithy_schema::ShapeType::Structure,
1916            &[&MEMBER],
1917        );
1918
1919        for bad in [
1920            &br#"{"":t!!!}"#[..],
1921            &br#"{"":f!!!!}"#[..],
1922            &br#"{"":n!!!}"#[..],
1923        ] {
1924            let mut deser = JsonDeserializer::new(bad, Arc::new(JsonCodecSettings::default()));
1925            let result = deser.read_struct(&SCHEMA, &mut |_, _| Ok(()));
1926            assert!(
1927                matches!(result, Err(SerdeError::InvalidInput { .. })),
1928                "expected InvalidInput for malformed input {:?}, got {:?}",
1929                std::str::from_utf8(bad).unwrap_or("<non-utf8>"),
1930                result
1931            );
1932        }
1933    }
1934
1935    #[test]
1936    fn regression_truncated_struct_does_not_infinite_loop() {
1937        // Reviewer follow-up: `read_struct` now uses an explicit `None`
1938        // arm inside the loop, so a truncated `{` input is rejected as
1939        // InvalidInput instead of relying on a downstream check to
1940        // catch it. This mirrors the fix for `read_list`.
1941        use aws_smithy_schema::Schema;
1942
1943        static MEMBER: Schema = Schema::new_member(
1944            aws_smithy_schema::shape_id!("test", "S"),
1945            aws_smithy_schema::ShapeType::String,
1946            "m",
1947            0,
1948        );
1949        static SCHEMA: Schema = Schema::new_struct(
1950            aws_smithy_schema::shape_id!("test", "S"),
1951            aws_smithy_schema::ShapeType::Structure,
1952            &[&MEMBER],
1953        );
1954
1955        let mut deser = JsonDeserializer::new(b"{", Arc::new(JsonCodecSettings::default()));
1956        let err = deser
1957            .read_struct(&SCHEMA, &mut |_, _| Ok(()))
1958            .expect_err("truncated struct input must be rejected");
1959        assert!(
1960            matches!(err, SerdeError::InvalidInput { .. }),
1961            "expected InvalidInput, got {err:?}"
1962        );
1963    }
1964
1965    #[test]
1966    fn regression_truncated_map_does_not_infinite_loop() {
1967        // Same as above but for `read_map`.
1968        let mut deser = JsonDeserializer::new(b"{", Arc::new(JsonCodecSettings::default()));
1969        let err = deser
1970            .read_map(dummy_schema(), &mut |_, _| Ok(()))
1971            .expect_err("truncated map input must be rejected");
1972        assert!(
1973            matches!(err, SerdeError::InvalidInput { .. }),
1974            "expected InvalidInput, got {err:?}"
1975        );
1976    }
1977
1978    // ---- Recursion-depth guard tests ----
1979    //
1980    // These verify that deeply-nested payloads produce a clean `SerdeError`
1981    // instead of a stack overflow. They exercise the same recursion pattern
1982    // an attacker would: a JSON object/list/map/document nested far past the
1983    // `MAX_DESERIALIZE_DEPTH` limit.
1984
1985    /// Builds `open.repeat(n) + close.repeat(n)`. For lists this is valid JSON
1986    /// (nested empty arrays). For objects this is malformed, but the depth
1987    /// guard fires before the malformed region is reached so reject-tests work
1988    /// either way.
1989    fn build_nested(open: &str, close: &str, n: usize) -> Vec<u8> {
1990        build_nested_with_inner(open, "", close, n)
1991    }
1992
1993    /// Builds `open.repeat(n) + inner + close.repeat(n)` — always valid JSON
1994    /// given a valid `inner`. Used for accept-under-limit tests.
1995    fn build_nested_with_inner(open: &str, inner: &str, close: &str, n: usize) -> Vec<u8> {
1996        let mut out = String::with_capacity(open.len() * n + inner.len() + close.len() * n);
1997        for _ in 0..n {
1998            out.push_str(open);
1999        }
2000        out.push_str(inner);
2001        for _ in 0..n {
2002            out.push_str(close);
2003        }
2004        out.into_bytes()
2005    }
2006
2007    /// A self-referential struct schema: member `"a"` points back to the parent
2008    /// schema. This is the minimum schema needed to exercise the depth guard
2009    /// through `read_struct`'s consumer-callback path (without it, unknown
2010    /// members go through `skip_value` which is iterative).
2011    fn recursive_struct_schema() -> &'static Schema {
2012        static MEMBER_A: Schema = Schema::new_member(
2013            aws_smithy_schema::shape_id!("test", "Rec"),
2014            aws_smithy_schema::ShapeType::Structure,
2015            "a",
2016            0,
2017        );
2018        static RECURSIVE: Schema = Schema::new_struct(
2019            aws_smithy_schema::shape_id!("test", "Rec"),
2020            aws_smithy_schema::ShapeType::Structure,
2021            &[&MEMBER_A],
2022        );
2023        &RECURSIVE
2024    }
2025
2026    /// Consumer that re-enters `read_struct` to exercise the depth guard.
2027    fn recursive_struct_consumer(
2028        _member: &Schema,
2029        deser: &mut dyn ShapeDeserializer,
2030    ) -> Result<(), SerdeError> {
2031        deser.read_struct(recursive_struct_schema(), &mut recursive_struct_consumer)
2032    }
2033
2034    /// Consumer that re-enters `read_list` to exercise the depth guard.
2035    fn recursive_list_consumer(deser: &mut dyn ShapeDeserializer) -> Result<(), SerdeError> {
2036        deser.read_list(dummy_schema(), &mut recursive_list_consumer)
2037    }
2038
2039    /// Consumer that re-enters `read_map` to exercise the depth guard.
2040    fn recursive_map_consumer(
2041        _key: String,
2042        deser: &mut dyn ShapeDeserializer,
2043    ) -> Result<(), SerdeError> {
2044        deser.read_map(dummy_schema(), &mut recursive_map_consumer)
2045    }
2046
2047    fn assert_depth_error(err: SerdeError) {
2048        match err {
2049            SerdeError::Custom { ref message } => {
2050                assert!(
2051                    message.contains("maximum nesting depth exceeded"),
2052                    "wrong error message: {message}"
2053                );
2054            }
2055            other => panic!("expected Custom depth error, got {other:?}"),
2056        }
2057    }
2058
2059    #[test]
2060    fn depth_limit_read_struct_rejects_deeply_nested() {
2061        // 200 levels — well past the 128 limit. The depth guard fires at
2062        // level 129 before we reach the ending, so the payload doesn't need
2063        // to be valid JSON all the way through.
2064        let payload = build_nested(r#"{"a":"#, "}", 200);
2065        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2066        let err = deser
2067            .read_struct(recursive_struct_schema(), &mut recursive_struct_consumer)
2068            .expect_err("deeply nested struct must be rejected");
2069        assert_depth_error(err);
2070    }
2071
2072    #[test]
2073    fn depth_limit_read_struct_accepts_under_limit() {
2074        // 100 levels of `{"a":` wrapped around an empty `{}` → 101 actual
2075        // `read_struct` calls, which is under the 128 limit.
2076        let payload = build_nested_with_inner(r#"{"a":"#, "{}", "}", 100);
2077        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2078        deser
2079            .read_struct(recursive_struct_schema(), &mut recursive_struct_consumer)
2080            .expect("100-level nesting should succeed");
2081    }
2082
2083    #[test]
2084    fn depth_limit_read_list_rejects_deeply_nested() {
2085        let payload = build_nested("[", "]", 200);
2086        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2087        let err = deser
2088            .read_list(dummy_schema(), &mut recursive_list_consumer)
2089            .expect_err("deeply nested list must be rejected");
2090        assert_depth_error(err);
2091    }
2092
2093    #[test]
2094    fn depth_limit_read_list_accepts_under_limit() {
2095        // `[[[...[]]]]` — 100 nested empty lists is valid JSON on its own.
2096        let payload = build_nested("[", "]", 100);
2097        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2098        deser
2099            .read_list(dummy_schema(), &mut recursive_list_consumer)
2100            .expect("100-level nesting should succeed");
2101    }
2102
2103    #[test]
2104    fn depth_limit_read_map_rejects_deeply_nested() {
2105        let payload = build_nested(r#"{"k":"#, "}", 200);
2106        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2107        let err = deser
2108            .read_map(dummy_schema(), &mut recursive_map_consumer)
2109            .expect_err("deeply nested map must be rejected");
2110        assert_depth_error(err);
2111    }
2112
2113    #[test]
2114    fn depth_limit_read_map_accepts_under_limit() {
2115        let payload = build_nested_with_inner(r#"{"k":"#, "{}", "}", 100);
2116        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2117        deser
2118            .read_map(dummy_schema(), &mut recursive_map_consumer)
2119            .expect("100-level nesting should succeed");
2120    }
2121
2122    #[test]
2123    fn depth_limit_read_document_rejects_deeply_nested_object() {
2124        // `read_document` is directly self-recursive on `{` and `[` branches;
2125        // no external consumer is needed.
2126        let payload = build_nested(r#"{"k":"#, "}", 200);
2127        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2128        let err = deser
2129            .read_document(dummy_schema())
2130            .expect_err("deeply nested document must be rejected");
2131        assert_depth_error(err);
2132    }
2133
2134    #[test]
2135    fn depth_limit_read_document_rejects_deeply_nested_array() {
2136        let payload = build_nested("[", "]", 200);
2137        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2138        let err = deser
2139            .read_document(dummy_schema())
2140            .expect_err("deeply nested document array must be rejected");
2141        assert_depth_error(err);
2142    }
2143
2144    #[test]
2145    fn depth_limit_read_document_accepts_under_limit() {
2146        let payload = build_nested_with_inner(r#"{"k":"#, "{}", "}", 100);
2147        let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2148        deser
2149            .read_document(dummy_schema())
2150            .expect("100-level nesting should succeed");
2151    }
2152
2153    #[test]
2154    fn depth_limit_respects_custom_max_depth_setting() {
2155        // A custom, tighter limit trips before the default 128 would.
2156        let settings = JsonCodecSettings::builder().max_depth(16).build();
2157
2158        // 20 levels exceeds the custom limit of 16.
2159        let payload = build_nested("[", "]", 20);
2160        let mut deser = JsonDeserializer::new(&payload, Arc::new(settings));
2161        let err = deser
2162            .read_list(dummy_schema(), &mut recursive_list_consumer)
2163            .expect_err("20-level nesting must exceed custom limit of 16");
2164        assert_depth_error(err);
2165
2166        // 10 levels fits inside the custom limit.
2167        let settings_ok = JsonCodecSettings::builder().max_depth(16).build();
2168        let payload_ok = build_nested("[", "]", 10);
2169        let mut deser_ok = JsonDeserializer::new(&payload_ok, Arc::new(settings_ok));
2170        deser_ok
2171            .read_list(dummy_schema(), &mut recursive_list_consumer)
2172            .expect("10-level nesting should succeed under custom limit");
2173    }
2174}