Skip to main content

aws_smithy_cbor/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//! CBOR deserializer implementation.
7
8use aws_smithy_schema::serde::{capped_container_size, SerdeError, ShapeDeserializer};
9use aws_smithy_schema::{Schema, ShapeType};
10use aws_smithy_types::{BigDecimal, BigInteger, Blob, DateTime, Document};
11
12use crate::data::Type;
13
14/// CBOR deserializer that implements the ShapeDeserializer trait.
15///
16/// Wraps the existing `Decoder` which handles both definite and
17/// indefinite-length strings/blobs and includes millisecond-precision
18/// timestamp truncation.
19pub struct CborDeserializer<'a> {
20    decoder: crate::Decoder<'a>,
21    input_len: usize,
22    depth: u32,
23    max_depth: u32,
24    enforce_strictness: bool,
25}
26
27impl<'a> CborDeserializer<'a> {
28    pub(crate) fn new(input: &'a [u8], max_depth: u32) -> Self {
29        Self {
30            decoder: crate::Decoder::new(input),
31            input_len: input.len(),
32            depth: 0,
33            max_depth,
34            enforce_strictness: false,
35        }
36    }
37
38    /// Returns the current byte offset relative to the input slice supplied at construction.
39    ///
40    /// Calling this method does not advance the deserializer.
41    pub fn position(&self) -> usize {
42        self.decoder.position()
43    }
44
45    pub(crate) fn with_strictness(mut self, value: bool) -> Self {
46        self.enforce_strictness = value;
47        self
48    }
49
50    fn leave_container(&mut self) -> Result<(), SerdeError> {
51        self.depth -= 1;
52        if self.enforce_strictness && self.depth == 0 && self.decoder.position() != self.input_len {
53            return Err(SerdeError::invalid_input("trailing bytes after CBOR value"));
54        }
55        Ok(())
56    }
57
58    /// How many elements to pre-allocate for a container that declares `declared` of them,
59    /// with `remaining` input bytes left after its header. Each element takes at least
60    /// `min_bytes_per_element` bytes, so a truncated input that declares a huge count reserves
61    /// only as much as it could actually hold.
62    fn prealloc(declared: u64, remaining: usize, min_bytes_per_element: usize) -> usize {
63        let declared = usize::try_from(declared).unwrap_or(usize::MAX);
64        capped_container_size(declared.min(remaining / min_bytes_per_element))
65    }
66
67    fn remaining(&self) -> usize {
68        self.input_len.saturating_sub(self.decoder.position())
69    }
70
71    fn check_depth(&mut self) -> Result<(), SerdeError> {
72        self.depth += 1;
73        if self.depth > self.max_depth {
74            return Err(SerdeError::custom("maximum nesting depth exceeded"));
75        }
76        Ok(())
77    }
78
79    /// Returns true if the current CBOR item is a break code (end of indefinite container).
80    fn is_break(&self) -> bool {
81        matches!(self.decoder.datatype(), Ok(Type::Break))
82    }
83
84    /// Skips the break code at the end of an indefinite-length container.
85    fn consume_break(&mut self) -> Result<(), SerdeError> {
86        self.decoder.skip().map_err(deser_err)
87    }
88
89    /// Reads a list of items using the provided element reader, handling
90    /// both definite and indefinite-length arrays.
91    fn read_list_items<T>(
92        &mut self,
93        mut read_element: impl FnMut(&mut crate::Decoder<'_>) -> Result<T, SerdeError>,
94    ) -> Result<Vec<T>, SerdeError> {
95        self.check_depth()?;
96        let len = self.decoder.list().map_err(deser_err)?;
97        let is_indefinite = len.is_none();
98        let count = len.unwrap_or(0) as usize;
99        let mut out = Vec::with_capacity(Self::prealloc(len.unwrap_or(0), self.remaining(), 1));
100        let mut i = 0;
101        loop {
102            if !is_indefinite && i >= count {
103                break;
104            }
105            if is_indefinite && self.is_break() {
106                self.consume_break()?;
107                break;
108            }
109            out.push(read_element(&mut self.decoder)?);
110            i += 1;
111        }
112        self.leave_container()?;
113        Ok(out)
114    }
115}
116
117impl ShapeDeserializer for CborDeserializer<'_> {
118    fn read_struct(
119        &mut self,
120        schema: &Schema<'_>,
121        consumer: &mut dyn FnMut(&Schema<'_>, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
122    ) -> Result<(), SerdeError> {
123        // Empty input (e.g., empty HTTP response body) is treated as an empty struct
124        if self.decoder.position() >= self.input_len {
125            if self.enforce_strictness && self.depth != 0 {
126                return Err(SerdeError::invalid_input("truncated CBOR structure"));
127            }
128            return Ok(());
129        }
130        self.check_depth()?;
131        let len = self.decoder.map().map_err(deser_err)?;
132        let is_indefinite = len.is_none();
133        let count = len.unwrap_or(0) as usize;
134
135        let is_union = schema.shape_type() == ShapeType::Union;
136        let mut i = 0;
137        loop {
138            if !is_indefinite && i >= count {
139                break;
140            }
141            if is_indefinite && self.is_break() {
142                self.consume_break()?;
143                break;
144            }
145            let key = self.decoder.str().map_err(deser_err)?;
146            if self.enforce_strictness && !is_union && self.is_null() {
147                // A null structure member means the member is absent; the consumer
148                // never sees it, so builder defaults stay untouched. Unions are
149                // excluded: a null variant value must reach the consumer to be
150                // rejected there.
151                self.read_null()?;
152            } else if let Some(member_schema) = schema.member_schema(&key) {
153                consumer(member_schema, self)?;
154            } else if &*key == "__type" || self.is_null() {
155                // A protocol discriminator is never a member, and a `null` value is an
156                // absent member. Neither is reported.
157                self.decoder.skip().map_err(deser_err)?;
158            } else {
159                // Report the unknown key so a union consumer can act on it: the prelude
160                // `DOCUMENT` schema has no member index, so generated code lands in its
161                // fallthrough arm. The consumer may read the value with a typed read; if
162                // it does not, the position is unchanged and the value is skipped here.
163                let start = self.decoder.position();
164                consumer(&aws_smithy_schema::prelude::DOCUMENT, self)?;
165                if self.decoder.position() == start {
166                    self.decoder.skip().map_err(deser_err)?;
167                }
168            }
169            i += 1;
170        }
171        self.leave_container()?;
172        Ok(())
173    }
174
175    fn read_list(
176        &mut self,
177        _schema: &Schema<'_>,
178        consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
179    ) -> Result<(), SerdeError> {
180        self.check_depth()?;
181        let len = self.decoder.list().map_err(deser_err)?;
182        let is_indefinite = len.is_none();
183        let count = len.unwrap_or(0) as usize;
184
185        let mut i = 0;
186        loop {
187            if !is_indefinite && i >= count {
188                break;
189            }
190            if is_indefinite && self.is_break() {
191                self.consume_break()?;
192                break;
193            }
194            consumer(self)?;
195            i += 1;
196        }
197        self.leave_container()?;
198        Ok(())
199    }
200
201    fn read_map(
202        &mut self,
203        _schema: &Schema<'_>,
204        consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
205    ) -> Result<(), SerdeError> {
206        self.check_depth()?;
207        let len = self.decoder.map().map_err(deser_err)?;
208        let is_indefinite = len.is_none();
209        let count = len.unwrap_or(0) as usize;
210
211        let mut i = 0;
212        loop {
213            if !is_indefinite && i >= count {
214                break;
215            }
216            if is_indefinite && self.is_break() {
217                self.consume_break()?;
218                break;
219            }
220            let key = self.decoder.str().map_err(deser_err)?.into_owned();
221            consumer(key, self)?;
222            i += 1;
223        }
224        self.leave_container()?;
225        Ok(())
226    }
227
228    fn read_boolean(&mut self, _schema: &Schema<'_>) -> Result<bool, SerdeError> {
229        self.decoder.boolean().map_err(deser_err)
230    }
231
232    fn read_byte(&mut self, _schema: &Schema<'_>) -> Result<i8, SerdeError> {
233        self.decoder.byte().map_err(deser_err)
234    }
235
236    fn read_short(&mut self, _schema: &Schema<'_>) -> Result<i16, SerdeError> {
237        self.decoder.short().map_err(deser_err)
238    }
239
240    fn read_integer(&mut self, _schema: &Schema<'_>) -> Result<i32, SerdeError> {
241        self.decoder.integer().map_err(deser_err)
242    }
243
244    fn read_long(&mut self, _schema: &Schema<'_>) -> Result<i64, SerdeError> {
245        self.decoder.long().map_err(deser_err)
246    }
247
248    fn read_float(&mut self, _schema: &Schema<'_>) -> Result<f32, SerdeError> {
249        self.decoder.float().map_err(deser_err)
250    }
251
252    fn read_double(&mut self, _schema: &Schema<'_>) -> Result<f64, SerdeError> {
253        self.decoder.double().map_err(deser_err)
254    }
255
256    fn read_big_integer(&mut self, _schema: &Schema<'_>) -> Result<BigInteger, SerdeError> {
257        self.decoder.big_integer().map_err(deser_err)
258    }
259
260    fn read_big_decimal(&mut self, _schema: &Schema<'_>) -> Result<BigDecimal, SerdeError> {
261        self.decoder.big_decimal().map_err(deser_err)
262    }
263
264    fn read_string(&mut self, _schema: &Schema<'_>) -> Result<String, SerdeError> {
265        self.decoder
266            .str()
267            .map(|cow| cow.into_owned())
268            .map_err(deser_err)
269    }
270
271    fn read_blob(&mut self, _schema: &Schema<'_>) -> Result<Blob, SerdeError> {
272        self.decoder.blob().map_err(deser_err)
273    }
274
275    fn read_timestamp(&mut self, _schema: &Schema<'_>) -> Result<DateTime, SerdeError> {
276        self.decoder.timestamp().map_err(deser_err)
277    }
278
279    fn read_document(&mut self, _schema: &Schema<'_>) -> Result<Document, SerdeError> {
280        Err(SerdeError::unsupported(
281            "document types are not supported by rpcv2Cbor protocol",
282        ))
283    }
284
285    fn is_null(&self) -> bool {
286        matches!(self.decoder.datatype(), Ok(Type::Null))
287    }
288
289    fn read_null(&mut self) -> Result<(), SerdeError> {
290        self.decoder.null().map_err(deser_err)
291    }
292
293    /// CBOR shares one decoder across the whole map, so declining a value still has to
294    /// move past it. `Decoder::skip` consumes exactly one item, including nested
295    /// containers, which is the same mechanism used for unknown members.
296    fn skip_value(&mut self) -> Result<(), SerdeError> {
297        self.decoder.skip().map_err(deser_err)
298    }
299
300    fn container_size(&self) -> Option<usize> {
301        let mut peek = self.decoder.clone();
302        match peek.datatype().ok()? {
303            Type::Array | Type::ArrayIndef => peek.list().ok()?.map(|n| {
304                let remaining = self.input_len.saturating_sub(peek.position());
305                Self::prealloc(n, remaining, 1)
306            }),
307            Type::Map | Type::MapIndef => peek.map().ok()?.map(|n| {
308                let remaining = self.input_len.saturating_sub(peek.position());
309                Self::prealloc(n, remaining, 2)
310            }),
311            _ => None,
312        }
313    }
314
315    fn read_string_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<String>, SerdeError> {
316        self.read_list_items(|dec| dec.str().map(|c| c.into_owned()).map_err(deser_err))
317    }
318
319    fn read_blob_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<Blob>, SerdeError> {
320        self.read_list_items(|dec| dec.blob().map_err(deser_err))
321    }
322
323    fn read_integer_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<i32>, SerdeError> {
324        self.read_list_items(|dec| dec.integer().map_err(deser_err))
325    }
326
327    fn read_long_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<i64>, SerdeError> {
328        self.read_list_items(|dec| dec.long().map_err(deser_err))
329    }
330
331    fn read_string_string_map(
332        &mut self,
333        _schema: &Schema<'_>,
334    ) -> Result<std::collections::HashMap<String, String>, SerdeError> {
335        self.check_depth()?;
336        let len = self.decoder.map().map_err(deser_err)?;
337        let is_indefinite = len.is_none();
338        let count = len.unwrap_or(0) as usize;
339        let mut out = std::collections::HashMap::with_capacity(Self::prealloc(
340            len.unwrap_or(0),
341            self.remaining(),
342            // Each CBOR map entry needs at least one byte for its key and one for its value.
343            2,
344        ));
345        let mut i = 0;
346        loop {
347            if !is_indefinite && i >= count {
348                break;
349            }
350            if is_indefinite && self.is_break() {
351                self.consume_break()?;
352                break;
353            }
354            let key = self
355                .decoder
356                .str()
357                .map(|c| c.into_owned())
358                .map_err(deser_err)?;
359            let val = self
360                .decoder
361                .str()
362                .map(|c| c.into_owned())
363                .map_err(deser_err)?;
364            out.insert(key, val);
365            i += 1;
366        }
367        self.leave_container()?;
368        Ok(out)
369    }
370}
371
372fn deser_err(e: crate::decode::DeserializeError) -> SerdeError {
373    SerdeError::invalid_input(e.to_string())
374}
375
376#[cfg(test)]
377mod tests {
378    use super::*;
379    use aws_smithy_schema::codec::Codec;
380    use aws_smithy_schema::prelude::*;
381    use aws_smithy_schema::serde::{SerializableStruct, ShapeSerializer};
382    use aws_smithy_schema::{shape_id, ShapeType};
383
384    use crate::codec::{CborCodec, CborCodecSettings};
385
386    /// Helper: serialize with CborSerializer, then deserialize with CborDeserializer.
387    fn make_deser(f: impl FnOnce(&mut crate::codec::CborSerializer)) -> Vec<u8> {
388        use aws_smithy_schema::codec::FinishSerializer;
389        let codec = CborCodec::default();
390        let mut ser = codec.create_serializer();
391        f(&mut ser);
392        ser.finish()
393    }
394
395    #[test]
396    fn strict_null_struct_members_are_skipped_as_absent() {
397        static X: Schema =
398            Schema::new_member(shape_id!("test", "S", "x"), ShapeType::Integer, "x", 0);
399        static S: Schema = Schema::new_struct(shape_id!("test", "S"), ShapeType::Structure, &[&X]);
400        for strict in [false, true] {
401            let codec = CborCodec::new(CborCodecSettings::default().enforce_strictness(strict));
402            // Strict: the codec consumes the null itself, the consumer never runs
403            // and defaults stay untouched. Lenient: the consumer sees the null and
404            // fails here by requiring an integer.
405            let mut visited = false;
406            let result = codec.create_deserializer(b"\xa1\x61x\xf6").read_struct(
407                &S,
408                &mut |member, deser| {
409                    visited = true;
410                    deser.read_integer(member).map(|_| ())
411                },
412            );
413            assert_eq!(result.is_ok(), strict);
414            assert_eq!(visited, !strict);
415        }
416    }
417
418    #[test]
419    fn strict_top_level_containers() {
420        for input in [
421            vec![0xa0],
422            vec![0xbf, 0xff],
423            vec![0xa1, 0x61, b'x', 0x81, 0xa0],
424            vec![0xbf, 0x61, b'x', 0x9f, 0xbf, 0xff, 0xff, 0xff],
425        ] {
426            for strict in [false, true] {
427                let mut de = CborDeserializer::new(&input, 128).with_strictness(strict);
428                de.read_struct(&STRING, &mut |_, _| Ok(())).unwrap();
429                let mut trailing = input.clone();
430                trailing.push(0);
431                let mut de = CborDeserializer::new(&trailing, 128).with_strictness(strict);
432                assert_eq!(de.read_struct(&STRING, &mut |_, _| Ok(())).is_err(), strict);
433            }
434        }
435    }
436
437    /// Regression (worklist item 9): a declared container length is untrusted. A truncated
438    /// container that declares 2^64 - 1 elements must not reserve space for them.
439    #[test]
440    fn container_preallocation_is_bounded_by_remaining_input() {
441        // Array / map with an 8-byte length of u64::MAX and no elements.
442        for (header, element_bytes) in [(0x9bu8, 1usize), (0xbb, 2)] {
443            let mut bytes = vec![header];
444            bytes.extend_from_slice(&u64::MAX.to_be_bytes());
445            let de = CborDeserializer::new(&bytes, 128);
446            assert_eq!(de.container_size(), Some(0), "header {header:#x}");
447            // With 10 bytes after the header, at most 10 / element_bytes elements fit.
448            bytes.extend_from_slice(&[0u8; 10]);
449            let de = CborDeserializer::new(&bytes, 128);
450            assert_eq!(
451                de.container_size(),
452                Some(10 / element_bytes),
453                "header {header:#x}"
454            );
455        }
456        // A well-formed container still reports its declared size.
457        let mut enc = crate::Encoder::new(Vec::new());
458        enc.array(3);
459        for i in 0..3 {
460            enc.integer(i);
461        }
462        let bytes = enc.into_writer();
463        let de = CborDeserializer::new(&bytes, 128);
464        assert_eq!(de.container_size(), Some(3));
465        // The fast list path still decodes and rejects the truncated input.
466        let mut truncated = vec![0x9b];
467        truncated.extend_from_slice(&u64::MAX.to_be_bytes());
468        let mut de = CborDeserializer::new(&truncated, 128);
469        assert!(de.read_integer_list(&INTEGER).is_err());
470        assert_eq!(CborDeserializer::prealloc(u64::MAX, usize::MAX, 1), 10_000);
471    }
472
473    #[test]
474    fn test_read_boolean() {
475        let bytes = make_deser(|s| s.write_boolean(&BOOLEAN, true).unwrap());
476        let mut de = CborDeserializer::new(&bytes, 128);
477        assert!(de.read_boolean(&BOOLEAN).unwrap());
478    }
479
480    #[test]
481    fn test_read_byte() {
482        let bytes = make_deser(|s| s.write_byte(&BYTE, -42).unwrap());
483        let mut de = CborDeserializer::new(&bytes, 128);
484        assert_eq!(de.read_byte(&BYTE).unwrap(), -42);
485    }
486
487    #[test]
488    fn test_read_short() {
489        let bytes = make_deser(|s| s.write_short(&SHORT, 1234).unwrap());
490        let mut de = CborDeserializer::new(&bytes, 128);
491        assert_eq!(de.read_short(&SHORT).unwrap(), 1234);
492    }
493
494    #[test]
495    fn test_read_integer() {
496        let bytes = make_deser(|s| s.write_integer(&INTEGER, -99999).unwrap());
497        let mut de = CborDeserializer::new(&bytes, 128);
498        assert_eq!(de.read_integer(&INTEGER).unwrap(), -99999);
499    }
500
501    #[test]
502    fn test_read_long() {
503        let bytes = make_deser(|s| s.write_long(&LONG, i64::MIN).unwrap());
504        let mut de = CborDeserializer::new(&bytes, 128);
505        assert_eq!(de.read_long(&LONG).unwrap(), i64::MIN);
506    }
507
508    #[test]
509    fn test_read_float_nan() {
510        let bytes = make_deser(|s| s.write_float(&FLOAT, f32::NAN).unwrap());
511        let mut de = CborDeserializer::new(&bytes, 128);
512        assert!(de.read_float(&FLOAT).unwrap().is_nan());
513    }
514
515    #[test]
516    fn test_read_double_neg_infinity() {
517        let bytes = make_deser(|s| s.write_double(&DOUBLE, f64::NEG_INFINITY).unwrap());
518        let mut de = CborDeserializer::new(&bytes, 128);
519        assert_eq!(de.read_double(&DOUBLE).unwrap(), f64::NEG_INFINITY);
520    }
521
522    #[test]
523    fn test_read_big_integer() {
524        let value = "18446744073709551616".parse().unwrap();
525        let bytes = make_deser(|s| s.write_big_integer(&BIG_INTEGER, &value).unwrap());
526        let mut de = CborDeserializer::new(&bytes, 128);
527        assert_eq!(de.read_big_integer(&BIG_INTEGER).unwrap(), value);
528    }
529
530    #[test]
531    fn test_read_big_decimal() {
532        let value = "273.15".parse().unwrap();
533        let bytes = make_deser(|s| s.write_big_decimal(&BIG_DECIMAL, &value).unwrap());
534        let mut de = CborDeserializer::new(&bytes, 128);
535        assert_eq!(de.read_big_decimal(&BIG_DECIMAL).unwrap(), value);
536    }
537
538    #[test]
539    fn test_read_string() {
540        let bytes = make_deser(|s| s.write_string(&STRING, "hello world").unwrap());
541        let mut de = CborDeserializer::new(&bytes, 128);
542        assert_eq!(de.read_string(&STRING).unwrap(), "hello world");
543    }
544
545    #[test]
546    fn test_read_blob() {
547        let blob = Blob::new(b"binary");
548        let bytes = make_deser(|s| s.write_blob(&BLOB, blob.clone()).unwrap());
549        let mut de = CborDeserializer::new(&bytes, 128);
550        assert_eq!(de.read_blob(&BLOB).unwrap(), blob);
551    }
552
553    #[test]
554    fn test_read_timestamp() {
555        let ts = DateTime::from_secs_f64(1700000000.123);
556        let bytes = make_deser(|s| s.write_timestamp(&TIMESTAMP, &ts).unwrap());
557        let mut de = CborDeserializer::new(&bytes, 128);
558        let decoded = de.read_timestamp(&TIMESTAMP).unwrap();
559        // Millisecond truncation
560        assert_eq!(decoded.subsec_nanos() % 1_000_000, 0);
561    }
562
563    #[test]
564    fn test_is_null() {
565        let bytes = make_deser(|s| s.write_null(&STRING).unwrap());
566        let de = CborDeserializer::new(&bytes, 128);
567        assert!(de.is_null());
568    }
569
570    #[test]
571    fn test_read_null() {
572        let bytes = make_deser(|s| s.write_null(&STRING).unwrap());
573        let mut de = CborDeserializer::new(&bytes, 128);
574        de.read_null().unwrap();
575    }
576
577    #[test]
578    fn test_read_struct() {
579        static NAME: Schema =
580            Schema::new_member(shape_id!("test", "S"), ShapeType::String, "name", 0);
581        static AGE: Schema =
582            Schema::new_member(shape_id!("test", "S"), ShapeType::Integer, "age", 1);
583        static SCHEMA: Schema =
584            Schema::new_struct(shape_id!("test", "S"), ShapeType::Structure, &[&NAME, &AGE]);
585
586        struct TestStruct;
587        impl SerializableStruct for TestStruct {
588            fn schema(&self) -> &Schema<'_> {
589                &SCHEMA
590            }
591
592            fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
593                s.write_string(&NAME, "Bob")?;
594                s.write_integer(&AGE, 25)?;
595                Ok(())
596            }
597        }
598
599        let bytes = make_deser(|s| s.write_struct(&SCHEMA, &TestStruct).unwrap());
600        let mut de = CborDeserializer::new(&bytes, 128);
601        let mut name = String::new();
602        let mut age = 0i32;
603        de.read_struct(&SCHEMA, &mut |member, d| {
604            match member.member_name() {
605                Some("name") => name = d.read_string(member)?,
606                Some("age") => age = d.read_integer(member)?,
607                _ => {}
608            }
609            Ok(())
610        })
611        .unwrap();
612        assert_eq!(name, "Bob");
613        assert_eq!(age, 25);
614    }
615
616    #[test]
617    fn test_read_struct_unknown_members_skipped() {
618        // Build CBOR with an extra field not in the schema
619        let mut enc = crate::Encoder::new(Vec::new());
620        enc.begin_map()
621            .str("name")
622            .str("Alice")
623            .str("unknown_field")
624            .integer(999)
625            .str("age")
626            .integer(30)
627            .end();
628        let bytes = enc.into_writer();
629
630        static NAME: Schema =
631            Schema::new_member(shape_id!("test", "S"), ShapeType::String, "name", 0);
632        static AGE: Schema =
633            Schema::new_member(shape_id!("test", "S"), ShapeType::Integer, "age", 1);
634        static SCHEMA: Schema =
635            Schema::new_struct(shape_id!("test", "S"), ShapeType::Structure, &[&NAME, &AGE]);
636
637        let mut de = CborDeserializer::new(&bytes, 128);
638        let mut name = String::new();
639        let mut age = 0i32;
640        de.read_struct(&SCHEMA, &mut |member, d| {
641            match member.member_name() {
642                Some("name") => name = d.read_string(member)?,
643                Some("age") => age = d.read_integer(member)?,
644                _ => {}
645            }
646            Ok(())
647        })
648        .unwrap();
649        assert_eq!(name, "Alice");
650        assert_eq!(age, 30);
651    }
652
653    #[test]
654    fn test_read_struct_definite_length_map() {
655        // Services may send definite-length maps
656        let mut enc = crate::Encoder::new(Vec::new());
657        enc.map(1).str("name").str("Charlie");
658        let bytes = enc.into_writer();
659
660        static NAME: Schema =
661            Schema::new_member(shape_id!("test", "S"), ShapeType::String, "name", 0);
662        static SCHEMA: Schema =
663            Schema::new_struct(shape_id!("test", "S"), ShapeType::Structure, &[&NAME]);
664
665        let mut de = CborDeserializer::new(&bytes, 128);
666        let mut name = String::new();
667        de.read_struct(&SCHEMA, &mut |member, d| {
668            if member.member_name() == Some("name") {
669                name = d.read_string(member)?;
670            }
671            Ok(())
672        })
673        .unwrap();
674        assert_eq!(name, "Charlie");
675    }
676
677    #[test]
678    fn test_read_list() {
679        let bytes = make_deser(|s| {
680            let schema = Schema::new(shape_id!("test", "L"), ShapeType::List);
681            s.write_list(&schema, &|s| {
682                s.write_integer(&INTEGER, 10)?;
683                s.write_integer(&INTEGER, 20)?;
684                Ok(())
685            })
686            .unwrap()
687        });
688        let list_schema = Schema::new(shape_id!("test", "L"), ShapeType::List);
689        let mut de = CborDeserializer::new(&bytes, 128);
690        let mut items = Vec::new();
691        de.read_list(&list_schema, &mut |d| {
692            items.push(d.read_integer(&INTEGER)?);
693            Ok(())
694        })
695        .unwrap();
696        assert_eq!(items, vec![10, 20]);
697    }
698
699    #[test]
700    fn test_read_map() {
701        let bytes = make_deser(|s| {
702            let schema = Schema::new(shape_id!("test", "M"), ShapeType::Map);
703            s.write_map(&schema, &|s| {
704                s.write_string(&STRING, "k1")?;
705                s.write_string(&STRING, "v1")?;
706                s.write_string(&STRING, "k2")?;
707                s.write_string(&STRING, "v2")?;
708                Ok(())
709            })
710            .unwrap()
711        });
712        let map_schema = Schema::new(shape_id!("test", "M"), ShapeType::Map);
713        let mut de = CborDeserializer::new(&bytes, 128);
714        let mut map = std::collections::HashMap::new();
715        de.read_map(&map_schema, &mut |key, d| {
716            map.insert(key, d.read_string(&STRING)?);
717            Ok(())
718        })
719        .unwrap();
720        assert_eq!(map.get("k1").unwrap(), "v1");
721        assert_eq!(map.get("k2").unwrap(), "v2");
722    }
723
724    #[test]
725    fn test_empty_input_treated_as_empty_struct() {
726        static SCHEMA: Schema =
727            Schema::new_struct(shape_id!("test", "S"), ShapeType::Structure, &[]);
728        let mut de = CborDeserializer::new(&[], 128);
729        de.read_struct(&SCHEMA, &mut |_, _| Ok(())).unwrap();
730    }
731
732    #[test]
733    fn test_container_size_definite() {
734        let mut enc = crate::Encoder::new(Vec::new());
735        enc.array(5);
736        for i in 0..5 {
737            enc.integer(i);
738        }
739        let bytes = enc.into_writer();
740        let de = CborDeserializer::new(&bytes, 128);
741        assert_eq!(de.container_size(), Some(5));
742    }
743
744    #[test]
745    fn test_container_size_indefinite_returns_none() {
746        let mut enc = crate::Encoder::new(Vec::new());
747        enc.begin_array();
748        let bytes = enc.into_writer();
749        let de = CborDeserializer::new(&bytes, 128);
750        assert_eq!(de.container_size(), None);
751    }
752
753    #[test]
754    fn test_depth_limit_rejects_deeply_nested() {
755        // Build deeply nested maps: {"a": {"a": {"a": ...}}}
756        let mut enc = crate::Encoder::new(Vec::new());
757        for _ in 0..200 {
758            enc.begin_map().str("a");
759        }
760        enc.begin_map().end(); // innermost empty map
761        for _ in 0..200 {
762            enc.end();
763        }
764        let bytes = enc.into_writer();
765
766        static MEMBER: Schema =
767            Schema::new_member(shape_id!("test", "R"), ShapeType::Structure, "a", 0);
768        static SCHEMA: Schema =
769            Schema::new_struct(shape_id!("test", "R"), ShapeType::Structure, &[&MEMBER]);
770
771        let mut de = CborDeserializer::new(&bytes, 128);
772        fn recursive_consumer(
773            _member: &Schema<'_>,
774            deser: &mut dyn ShapeDeserializer,
775        ) -> Result<(), SerdeError> {
776            static MEMBER: Schema =
777                Schema::new_member(shape_id!("test", "R"), ShapeType::Structure, "a", 0);
778            static SCHEMA: Schema =
779                Schema::new_struct(shape_id!("test", "R"), ShapeType::Structure, &[&MEMBER]);
780            deser.read_struct(&SCHEMA, &mut recursive_consumer)
781        }
782        let result = de.read_struct(&SCHEMA, &mut recursive_consumer);
783        assert!(result.is_err());
784        assert!(result.unwrap_err().to_string().contains("depth"));
785    }
786
787    #[test]
788    fn test_depth_limit_accepts_under_limit() {
789        // 10 levels of nesting — well under 128
790        let mut enc = crate::Encoder::new(Vec::new());
791        for _ in 0..10 {
792            enc.begin_map().str("a");
793        }
794        enc.begin_map().end();
795        for _ in 0..10 {
796            enc.end();
797        }
798        let bytes = enc.into_writer();
799
800        static MEMBER: Schema =
801            Schema::new_member(shape_id!("test", "R"), ShapeType::Structure, "a", 0);
802        static SCHEMA: Schema =
803            Schema::new_struct(shape_id!("test", "R"), ShapeType::Structure, &[&MEMBER]);
804
805        let mut de = CborDeserializer::new(&bytes, 128);
806        fn recursive_consumer(
807            _member: &Schema<'_>,
808            deser: &mut dyn ShapeDeserializer,
809        ) -> Result<(), SerdeError> {
810            static MEMBER: Schema =
811                Schema::new_member(shape_id!("test", "R"), ShapeType::Structure, "a", 0);
812            static SCHEMA: Schema =
813                Schema::new_struct(shape_id!("test", "R"), ShapeType::Structure, &[&MEMBER]);
814            deser.read_struct(&SCHEMA, &mut recursive_consumer)
815        }
816        de.read_struct(&SCHEMA, &mut recursive_consumer).unwrap();
817    }
818
819    #[test]
820    fn test_read_struct_with_null_optional_members() {
821        // Struct with null value for an optional member — null should be skipped
822        let mut enc = crate::Encoder::new(Vec::new());
823        enc.begin_map()
824            .str("name")
825            .str("Alice")
826            .str("age")
827            .null()
828            .end();
829        let bytes = enc.into_writer();
830
831        static NAME: Schema =
832            Schema::new_member(shape_id!("test", "S"), ShapeType::String, "name", 0);
833        static AGE: Schema =
834            Schema::new_member(shape_id!("test", "S"), ShapeType::Integer, "age", 1);
835        static SCHEMA: Schema =
836            Schema::new_struct(shape_id!("test", "S"), ShapeType::Structure, &[&NAME, &AGE]);
837
838        let mut de = CborDeserializer::new(&bytes, 128);
839        let mut name: Option<String> = None;
840        let mut age: Option<i32> = None;
841        de.read_struct(&SCHEMA, &mut |member, d| {
842            match member.member_name() {
843                Some("name") => {
844                    if !d.is_null() {
845                        name = Some(d.read_string(member)?);
846                    } else {
847                        d.read_null()?;
848                    }
849                }
850                Some("age") => {
851                    if !d.is_null() {
852                        age = Some(d.read_integer(member)?);
853                    } else {
854                        d.read_null()?;
855                    }
856                }
857                _ => {}
858            }
859            Ok(())
860        })
861        .unwrap();
862        assert_eq!(name, Some("Alice".to_string()));
863        assert_eq!(age, None);
864    }
865
866    #[test]
867    fn test_read_sparse_list_with_nulls() {
868        // @sparse list: [1, null, 3] — null should not be skipped
869        let mut enc = crate::Encoder::new(Vec::new());
870        enc.begin_array().integer(1).null().integer(3).end();
871        let bytes = enc.into_writer();
872
873        let list_schema = Schema::new(shape_id!("test", "L"), ShapeType::List);
874        let mut de = CborDeserializer::new(&bytes, 128);
875        let mut items: Vec<Option<i32>> = Vec::new();
876        de.read_list(&list_schema, &mut |d| {
877            if d.is_null() {
878                d.read_null()?;
879                items.push(None);
880            } else {
881                items.push(Some(d.read_integer(&INTEGER)?));
882            }
883            Ok(())
884        })
885        .unwrap();
886        assert_eq!(items, vec![Some(1), None, Some(3)]);
887    }
888
889    #[test]
890    fn test_read_sparse_map_with_nulls() {
891        // @sparse map: {"a": "hello", "b": null} — null should not be skipped
892        let mut enc = crate::Encoder::new(Vec::new());
893        enc.begin_map().str("a").str("hello").str("b").null().end();
894        let bytes = enc.into_writer();
895
896        let map_schema = Schema::new(shape_id!("test", "M"), ShapeType::Map);
897        let mut de = CborDeserializer::new(&bytes, 128);
898        let mut map: std::collections::HashMap<String, Option<String>> =
899            std::collections::HashMap::new();
900        de.read_map(&map_schema, &mut |key, d| {
901            if d.is_null() {
902                d.read_null()?;
903                map.insert(key, None);
904            } else {
905                map.insert(key, Some(d.read_string(&STRING)?));
906            }
907            Ok(())
908        })
909        .unwrap();
910        assert_eq!(map.get("a"), Some(&Some("hello".to_string())));
911        assert_eq!(map.get("b"), Some(&None));
912    }
913
914    // --- Union deserialization: union-in-union, empty / unknown-only union ---
915    //
916    // Mirrors the generated union deserializer: read_struct over a
917    // ShapeType::Union schema, dispatch on member_index(), and ok_or_else when
918    // no variant was set. A union-valued member recurses into the inner union's
919    // own deserialize (its own self-contained read_struct). Because the CBOR
920    // read_struct reads its own map header and terminator, the recursion is
921    // safe and an empty / unknown-only union produces a clean error, never a
922    // panic or a corrupted stream. Both definite- and indefinite-length maps
923    // are exercised.
924
925    static U_INNER_LAMBDA: Schema = Schema::new_member(
926        shape_id!("test", "InnerUnion"),
927        ShapeType::String,
928        "lambda",
929        0,
930    );
931    static U_INNER_SCHEMA: Schema = Schema::new_struct(
932        shape_id!("test", "InnerUnion"),
933        ShapeType::Union,
934        &[&U_INNER_LAMBDA],
935    );
936    static U_OUTER_MCP: Schema =
937        Schema::new_member(shape_id!("test", "OuterUnion"), ShapeType::Union, "mcp", 0);
938    static U_OUTER_SCHEMA: Schema = Schema::new_struct(
939        shape_id!("test", "OuterUnion"),
940        ShapeType::Union,
941        &[&U_OUTER_MCP],
942    );
943
944    #[derive(Debug, PartialEq)]
945    enum InnerUnion {
946        Lambda(String),
947        Unknown,
948    }
949    #[derive(Debug, PartialEq)]
950    enum OuterUnion {
951        Mcp(InnerUnion),
952        Unknown,
953    }
954
955    fn deser_inner_union(deser: &mut dyn ShapeDeserializer) -> Result<InnerUnion, SerdeError> {
956        let mut result: Option<InnerUnion> = None;
957        deser.read_struct(&U_INNER_SCHEMA, &mut |member, d| {
958            result = Some(match member.member_index() {
959                Some(0) => InnerUnion::Lambda(d.read_string(member)?),
960                _ => InnerUnion::Unknown,
961            });
962            Ok(())
963        })?;
964        result.ok_or_else(|| SerdeError::custom("expected a union variant"))
965    }
966
967    fn deser_outer_union(deser: &mut dyn ShapeDeserializer) -> Result<OuterUnion, SerdeError> {
968        let mut result: Option<OuterUnion> = None;
969        deser.read_struct(&U_OUTER_SCHEMA, &mut |member, d| {
970            result = Some(match member.member_index() {
971                // union-valued member recurses into the inner union's own deserialize
972                Some(0) => OuterUnion::Mcp(deser_inner_union(d)?),
973                _ => OuterUnion::Unknown,
974            });
975            Ok(())
976        })?;
977        result.ok_or_else(|| SerdeError::custom("expected a union variant"))
978    }
979
980    #[test]
981    fn union_in_union_definite_map() {
982        // outer {mcp: inner {lambda: "arn"}} as definite-length maps.
983        let mut e = crate::Encoder::new(Vec::new());
984        e.map(1)
985            .str("mcp")
986            .map(1)
987            .str("lambda")
988            .str("arn:aws:lambda:fn");
989        let bytes = e.into_writer();
990        let mut de = CborDeserializer::new(&bytes, 128);
991        assert_eq!(
992            deser_outer_union(&mut de).expect("union-in-union must deserialize"),
993            OuterUnion::Mcp(InnerUnion::Lambda("arn:aws:lambda:fn".to_string()))
994        );
995    }
996
997    #[test]
998    fn union_in_union_indefinite_map() {
999        let mut e = crate::Encoder::new(Vec::new());
1000        e.begin_map()
1001            .str("mcp")
1002            .begin_map()
1003            .str("lambda")
1004            .str("arn:aws:lambda:fn")
1005            .end()
1006            .end();
1007        let bytes = e.into_writer();
1008        let mut de = CborDeserializer::new(&bytes, 128);
1009        assert_eq!(
1010            deser_outer_union(&mut de).expect("union-in-union (indefinite) must deserialize"),
1011            OuterUnion::Mcp(InnerUnion::Lambda("arn:aws:lambda:fn".to_string()))
1012        );
1013    }
1014
1015    static U_HOLDER_CHOICE: Schema =
1016        Schema::new_member(shape_id!("test", "Holder"), ShapeType::Union, "choice", 0);
1017    static U_HOLDER_TRAILING: Schema = Schema::new_member(
1018        shape_id!("test", "Holder"),
1019        ShapeType::String,
1020        "trailing",
1021        1,
1022    );
1023    static U_HOLDER_SCHEMA: Schema = Schema::new_struct(
1024        shape_id!("test", "Holder"),
1025        ShapeType::Structure,
1026        &[&U_HOLDER_CHOICE, &U_HOLDER_TRAILING],
1027    );
1028
1029    #[test]
1030    fn nested_union_leaves_decoder_positioned_for_trailing_sibling() {
1031        // {choice: {mcp: {lambda: "x"}}, trailing: "ok"} — the trailing sibling
1032        // must still parse after the nested union is read.
1033        let mut e = crate::Encoder::new(Vec::new());
1034        e.map(2)
1035            .str("choice")
1036            .map(1)
1037            .str("mcp")
1038            .map(1)
1039            .str("lambda")
1040            .str("x")
1041            .str("trailing")
1042            .str("ok");
1043        let bytes = e.into_writer();
1044        let mut de = CborDeserializer::new(&bytes, 128);
1045        let mut choice: Option<OuterUnion> = None;
1046        let mut trailing: Option<String> = None;
1047        de.read_struct(&U_HOLDER_SCHEMA, &mut |member, d| {
1048            match member.member_index() {
1049                Some(0) => choice = Some(deser_outer_union(d)?),
1050                Some(1) => trailing = Some(d.read_string(member)?),
1051                _ => {}
1052            }
1053            Ok(())
1054        })
1055        .expect("holder with a nested union must parse");
1056        assert_eq!(
1057            choice,
1058            Some(OuterUnion::Mcp(InnerUnion::Lambda("x".to_string())))
1059        );
1060        assert_eq!(
1061            trailing,
1062            Some("ok".to_string()),
1063            "trailing sibling must survive: the nested union read must leave the decoder positioned correctly"
1064        );
1065    }
1066
1067    #[test]
1068    fn empty_union_definite_map_yields_clean_error() {
1069        let mut e = crate::Encoder::new(Vec::new());
1070        e.map(0);
1071        let bytes = e.into_writer();
1072        let mut de = CborDeserializer::new(&bytes, 128);
1073        let err = deser_inner_union(&mut de).unwrap_err();
1074        assert!(
1075            err.to_string().contains("expected a union variant"),
1076            "empty definite union map must be a clean error, got {err:?}"
1077        );
1078    }
1079
1080    #[test]
1081    fn empty_union_indefinite_map_yields_clean_error() {
1082        let mut e = crate::Encoder::new(Vec::new());
1083        e.begin_map().end();
1084        let bytes = e.into_writer();
1085        let mut de = CborDeserializer::new(&bytes, 128);
1086        let err = deser_inner_union(&mut de).unwrap_err();
1087        assert!(
1088            err.to_string().contains("expected a union variant"),
1089            "empty indefinite union map must be a clean error, got {err:?}"
1090        );
1091    }
1092
1093    #[test]
1094    fn union_with_only_unknown_member_is_reported_to_the_consumer() {
1095        // A union map whose sole key matches no member is reported to the
1096        // consumer as an unknown member; this consumer maps it to `Unknown`
1097        // (as a client would) and the value is skipped for it.
1098        let mut e = crate::Encoder::new(Vec::new());
1099        e.map(1).str("zzz").str("ignored");
1100        let bytes = e.into_writer();
1101        let mut de = CborDeserializer::new(&bytes, 128);
1102        assert_eq!(deser_inner_union(&mut de).unwrap(), InnerUnion::Unknown);
1103    }
1104
1105    #[test]
1106    fn list_of_union_in_union() {
1107        let mut e = crate::Encoder::new(Vec::new());
1108        e.array(2)
1109            .map(1)
1110            .str("mcp")
1111            .map(1)
1112            .str("lambda")
1113            .str("a")
1114            .map(1)
1115            .str("mcp")
1116            .map(1)
1117            .str("lambda")
1118            .str("b");
1119        let bytes = e.into_writer();
1120        let list_schema = Schema::new(shape_id!("test", "L"), ShapeType::List);
1121        let mut de = CborDeserializer::new(&bytes, 128);
1122        let mut out = Vec::new();
1123        de.read_list(&list_schema, &mut |el| {
1124            out.push(deser_outer_union(el)?);
1125            Ok(())
1126        })
1127        .expect("list of union-in-union must deserialize");
1128        assert_eq!(
1129            out,
1130            vec![
1131                OuterUnion::Mcp(InnerUnion::Lambda("a".to_string())),
1132                OuterUnion::Mcp(InnerUnion::Lambda("b".to_string())),
1133            ]
1134        );
1135    }
1136
1137    #[test]
1138    fn null_valued_union_member_yields_clean_error_not_panic() {
1139        // Unlike JSON (whose read_struct skips explicit-null members before
1140        // dispatching), CBOR read_struct passes the null value straight to the
1141        // consumer. A realistic CBOR union carries exactly one non-null member,
1142        // so a null-valued member is malformed; the generated deserializer
1143        // reads it with the variant's typed read_* and gets a clean type error.
1144        // The important property for bug parity is that this is a clean error,
1145        // never a panic or a corrupted stream.
1146        let mut e = crate::Encoder::new(Vec::new());
1147        e.map(1).str("lambda").null();
1148        let bytes = e.into_writer();
1149        let mut de = CborDeserializer::new(&bytes, 128);
1150        assert!(
1151            deser_inner_union(&mut de).is_err(),
1152            "a null-valued union member must be a clean error, not a panic"
1153        );
1154    }
1155
1156    // --- Required value-type members are serialized even when equal to the
1157    // zero/default (bool false). Mirrors the generated non-optional branch
1158    // `{ let val = &self.x; ser.write_boolean(..) }` (unconditional, never a
1159    // skip-if-default). Shapes the ELB
1160    // `LoadBalancerAttributes { ConnectionDraining { Enabled = false } }` case:
1161    // the nested structure member is present and `enabled=false` is on the wire.
1162
1163    static B_CD_ENABLED: Schema = Schema::new_member(
1164        shape_id!("test", "ConnectionDraining$enabled"),
1165        ShapeType::Boolean,
1166        "enabled",
1167        0,
1168    );
1169    static B_CD_SCHEMA: Schema = Schema::new_struct(
1170        shape_id!("test", "ConnectionDraining"),
1171        ShapeType::Structure,
1172        &[&B_CD_ENABLED],
1173    );
1174    static B_LBA_CD: Schema = Schema::new_member(
1175        shape_id!("test", "LoadBalancerAttributes$connectionDraining"),
1176        ShapeType::Structure,
1177        "connectionDraining",
1178        0,
1179    );
1180    static B_LBA_SCHEMA: Schema = Schema::new_struct(
1181        shape_id!("test", "LoadBalancerAttributes"),
1182        ShapeType::Structure,
1183        &[&B_LBA_CD],
1184    );
1185
1186    struct BConnectionDraining {
1187        enabled: bool,
1188    }
1189    impl SerializableStruct for BConnectionDraining {
1190        fn schema(&self) -> &Schema<'_> {
1191            &B_CD_SCHEMA
1192        }
1193
1194        fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1195            let val = &self.enabled;
1196            s.write_boolean(&B_CD_ENABLED, *val)
1197        }
1198    }
1199    struct BLoadBalancerAttributes {
1200        connection_draining: BConnectionDraining,
1201    }
1202    impl SerializableStruct for BLoadBalancerAttributes {
1203        fn schema(&self) -> &Schema<'_> {
1204            &B_LBA_SCHEMA
1205        }
1206
1207        fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1208            s.write_struct(&B_LBA_CD, &self.connection_draining)
1209        }
1210    }
1211
1212    #[test]
1213    fn required_value_type_false_bool_is_serialized() {
1214        let bytes = make_deser(|s| {
1215            s.write_struct(
1216                &B_LBA_SCHEMA,
1217                &BLoadBalancerAttributes {
1218                    connection_draining: BConnectionDraining { enabled: false },
1219                },
1220            )
1221            .unwrap()
1222        });
1223        // Read back: `enabled=false` (Some) proves the member was written to the
1224        // wire; if it had been dropped, the inner consumer would never fire.
1225        let mut de = CborDeserializer::new(&bytes, 128);
1226        let mut cd_seen = false;
1227        let mut enabled: Option<bool> = None;
1228        de.read_struct(&B_LBA_SCHEMA, &mut |m, d| {
1229            if m.member_name() == Some("connectionDraining") {
1230                cd_seen = true;
1231                d.read_struct(&B_CD_SCHEMA, &mut |im, id| {
1232                    if im.member_name() == Some("enabled") {
1233                        enabled = Some(id.read_boolean(im)?);
1234                    }
1235                    Ok(())
1236                })?;
1237            }
1238            Ok(())
1239        })
1240        .unwrap();
1241        assert!(cd_seen, "outer connectionDraining member must be present");
1242        assert_eq!(
1243            enabled,
1244            Some(false),
1245            "required value-type enabled=false must be serialized, not dropped"
1246        );
1247    }
1248}
1249
1250/// `read_struct` reports keys that name no member: the consumer is called with the
1251/// prelude `DOCUMENT` schema (no member index) positioned at the value. See the JSON
1252/// codec's `unknown_member_tests` for the full contract; these cover the CBOR specifics
1253/// (definite and indefinite maps, `null` values, `__type`).
1254#[cfg(test)]
1255mod unknown_member_tests {
1256    use super::*;
1257    use aws_smithy_schema::{shape_id, ShapeType};
1258
1259    static A: Schema<'static> =
1260        Schema::new_member(shape_id!("test", "S"), ShapeType::String, "a", 0);
1261    static S: Schema<'static> =
1262        Schema::new_struct(shape_id!("test", "S"), ShapeType::Structure, &[&A]);
1263
1264    /// CBOR has no document type, so a consumer that wants an unknown value reads it with
1265    /// a typed read. This one reads it as a string when asked.
1266    fn read_s(bytes: &[u8], read_unknown_value: bool) -> (Option<String>, Vec<Option<String>>) {
1267        let mut a = None;
1268        let mut unknown = Vec::new();
1269        CborDeserializer::new(bytes, 128)
1270            .read_struct(&S, &mut |member, d| {
1271                match member.member_index() {
1272                    Some(0) => a = Some(d.read_string(member)?),
1273                    _ => {
1274                        assert_eq!(member.shape_type(), ShapeType::Document);
1275                        unknown.push(if read_unknown_value {
1276                            Some(d.read_string(member)?)
1277                        } else {
1278                            None
1279                        });
1280                    }
1281                }
1282                Ok(())
1283            })
1284            .unwrap();
1285        (a, unknown)
1286    }
1287
1288    #[test]
1289    fn unknown_key_is_reported_and_the_consumer_can_read_the_value() {
1290        let mut e = crate::Encoder::new(Vec::new());
1291        e.map(2).str("zzz").str("value").str("a").str("x");
1292        let (a, unknown) = read_s(&e.into_writer(), true);
1293        assert_eq!(a.as_deref(), Some("x"));
1294        assert_eq!(unknown, [Some("value".to_string())]);
1295    }
1296
1297    #[test]
1298    fn unknown_key_value_is_skipped_when_not_read_in_definite_and_indefinite_maps() {
1299        let mut e = crate::Encoder::new(Vec::new());
1300        e.map(3)
1301            .str("zzz")
1302            .map(1)
1303            .str("deep")
1304            .array(2)
1305            .integer(1)
1306            .integer(2)
1307            .str("a")
1308            .str("x")
1309            .str("yyy")
1310            .str("tail");
1311        let (a, unknown) = read_s(&e.into_writer(), false);
1312        assert_eq!(a.as_deref(), Some("x"));
1313        assert_eq!(unknown.len(), 2);
1314
1315        let mut e = crate::Encoder::new(Vec::new());
1316        e.begin_map()
1317            .str("zzz")
1318            .array(2)
1319            .integer(1)
1320            .integer(2)
1321            .str("a")
1322            .str("x")
1323            .end();
1324        let (a, unknown) = read_s(&e.into_writer(), false);
1325        assert_eq!(a.as_deref(), Some("x"));
1326        assert_eq!(unknown.len(), 1);
1327    }
1328
1329    #[test]
1330    fn type_discriminator_and_null_values_are_not_reported() {
1331        let mut e = crate::Encoder::new(Vec::new());
1332        e.map(3)
1333            .str("__type")
1334            .str("ns#Foo")
1335            .str("zzz")
1336            .null()
1337            .str("a")
1338            .str("x");
1339        let (a, unknown) = read_s(&e.into_writer(), false);
1340        assert_eq!(a.as_deref(), Some("x"));
1341        assert!(unknown.is_empty());
1342    }
1343}
1344
1345/// Tests for the [`ShapeDeserializer::skip_value`] contract.
1346///
1347/// CBOR is cursor-based, so the override must consume exactly one complete item,
1348/// including nested containers. The scenario is the one the HTTP response composite
1349/// relies on: a body field for a member whose value came from a header is declined, and
1350/// the remaining members must still parse.
1351#[cfg(test)]
1352mod skip_value_contract {
1353    use super::*;
1354    use aws_smithy_schema::codec::{Codec, FinishSerializer};
1355    use aws_smithy_schema::prelude::*;
1356    use aws_smithy_schema::serde::{SerializableStruct, ShapeSerializer};
1357    use aws_smithy_schema::{shape_id, ShapeType};
1358
1359    use crate::codec::{CborCodec, CborSerializer};
1360
1361    // Member 0 is what the reader resolves the key "bound" to. The aggregate-typed
1362    // variants below share that member name so they serialize under the same key; only
1363    // the encoded value differs, which is all that matters for a skip.
1364    static BOUND: Schema =
1365        Schema::new_member(shape_id!("test", "Output"), ShapeType::String, "bound", 0);
1366    static BOUND_LIST: Schema =
1367        Schema::new_member(shape_id!("test", "Output"), ShapeType::List, "bound", 0);
1368    static BOUND_MAP: Schema =
1369        Schema::new_member(shape_id!("test", "Output"), ShapeType::Map, "bound", 0);
1370    static BODY: Schema =
1371        Schema::new_member(shape_id!("test", "Output"), ShapeType::String, "body", 1);
1372    static OUTPUT: Schema = Schema::new_struct(
1373        shape_id!("test", "Output"),
1374        ShapeType::Structure,
1375        &[&BOUND, &BODY],
1376    );
1377
1378    /// Encodes `{bound: <whatever `write_bound` writes>, body: "real"}`, then reads it
1379    /// back while skipping `bound`. Returns the value read for `body`, so a successful
1380    /// assertion proves the decoder resumed at exactly the right item.
1381    fn skip_bound_read_body(
1382        write_bound: fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
1383    ) -> Result<Option<String>, SerdeError> {
1384        struct Out(fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>);
1385        impl SerializableStruct for Out {
1386            fn schema(&self) -> &Schema<'_> {
1387                &OUTPUT
1388            }
1389            fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1390                (self.0)(s)?;
1391                s.write_string(&BODY, "real")
1392            }
1393        }
1394        let mut ser: CborSerializer = CborCodec::default().create_serializer();
1395        ser.write_struct(&OUTPUT, &Out(write_bound))?;
1396        let bytes = ser.finish();
1397
1398        let mut de = CborDeserializer::new(&bytes, 128);
1399        let mut body = None;
1400        de.read_struct(&OUTPUT, &mut |member, d| {
1401            match member.member_index() {
1402                Some(0) => d.skip_value()?,
1403                Some(1) => body = Some(d.read_string(member)?),
1404                _ => {}
1405            }
1406            Ok(())
1407        })?;
1408        Ok(body)
1409    }
1410
1411    fn assert_skips(name: &str, write: fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>) {
1412        let got =
1413            skip_bound_read_body(write).unwrap_or_else(|e| panic!("skipping {name} failed: {e}"));
1414        assert_eq!(
1415            got.as_deref(),
1416            Some("real"),
1417            "after skipping a {name}, `body` should still be read"
1418        );
1419    }
1420
1421    #[test]
1422    fn skipping_a_scalar_member_lets_the_rest_of_the_struct_parse() {
1423        assert_skips("string", |s| s.write_string(&BOUND, "skipped"));
1424        assert_skips("empty string", |s| s.write_string(&BOUND, ""));
1425        assert_skips("integer", |s| s.write_integer(&BOUND, -12345));
1426        assert_skips("long", |s| s.write_long(&BOUND, i64::MIN));
1427        assert_skips("double", |s| s.write_double(&BOUND, 1.5e300));
1428        assert_skips("boolean", |s| s.write_boolean(&BOUND, true));
1429        assert_skips("blob", |s| {
1430            s.write_blob(&BOUND, aws_smithy_types::Blob::new(vec![1, 2, 3]))
1431        });
1432        assert_skips("null", |s| s.write_null(&BOUND));
1433    }
1434
1435    #[test]
1436    fn skipping_an_aggregate_member_consumes_the_whole_container() {
1437        // `Decoder::skip` must consume the entire nested item, not just its header.
1438        assert_skips("list", |s| {
1439            s.write_list(&BOUND_LIST, &|s| {
1440                s.write_integer(&INTEGER, 1)?;
1441                s.write_integer(&INTEGER, 2)?;
1442                s.write_integer(&INTEGER, 3)
1443            })
1444        });
1445        assert_skips("empty list", |s| s.write_list(&BOUND_LIST, &|_| Ok(())));
1446        assert_skips("map", |s| {
1447            s.write_map(&BOUND_MAP, &|s| {
1448                s.write_string(&STRING, "k1")?;
1449                s.write_string(&STRING, "v1")?;
1450                s.write_string(&STRING, "k2")?;
1451                s.write_string(&STRING, "v2")
1452            })
1453        });
1454        assert_skips("empty map", |s| s.write_map(&BOUND_MAP, &|_| Ok(())));
1455        assert_skips("nested list of maps", |s| {
1456            s.write_list(&BOUND_LIST, &|s| {
1457                for _ in 0..2 {
1458                    s.write_map(&BOUND_MAP, &|s| {
1459                        s.write_string(&STRING, "deep")?;
1460                        s.write_list(&BOUND_LIST, &|s| {
1461                            s.write_integer(&INTEGER, 1)?;
1462                            s.write_integer(&INTEGER, 2)
1463                        })
1464                    })?;
1465                }
1466                Ok(())
1467            })
1468        });
1469        // A nested map carrying a key with the same name as the member read next:
1470        // skipping must not surface it to the consumer.
1471        assert_skips("map containing a decoy `body` key", |s| {
1472            s.write_map(&BOUND_MAP, &|s| {
1473                s.write_string(&STRING, "body")?;
1474                s.write_string(&STRING, "decoy")
1475            })
1476        });
1477    }
1478
1479    #[test]
1480    fn skip_value_on_an_unknown_member_does_not_double_skip() {
1481        // `read_struct` skips the unknown-member value only when the consumer left the
1482        // decoder position untouched. An explicit skip must not trigger a second one,
1483        // which would swallow the following member.
1484        static UNKNOWN: Schema =
1485            Schema::new_member(shape_id!("test", "Output"), ShapeType::List, "unknown", 0);
1486        struct Out;
1487        impl SerializableStruct for Out {
1488            fn schema(&self) -> &Schema<'_> {
1489                &OUTPUT
1490            }
1491            fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1492                s.write_list(&UNKNOWN, &|s| {
1493                    s.write_integer(&INTEGER, 1)?;
1494                    s.write_integer(&INTEGER, 2)
1495                })?;
1496                s.write_string(&BODY, "real")
1497            }
1498        }
1499        let mut ser: CborSerializer = CborCodec::default().create_serializer();
1500        ser.write_struct(&OUTPUT, &Out).unwrap();
1501        let bytes = ser.finish();
1502
1503        let mut de = CborDeserializer::new(&bytes, 128);
1504        let mut body = None;
1505        let mut saw_unknown = false;
1506        de.read_struct(&OUTPUT, &mut |member, d| {
1507            match member.member_index() {
1508                Some(1) => body = Some(d.read_string(member)?),
1509                // The index-less prelude `DOCUMENT` schema: the unknown-key report that
1510                // union consumers rely on.
1511                None => {
1512                    saw_unknown = true;
1513                    d.skip_value()?;
1514                }
1515                _ => {}
1516            }
1517            Ok(())
1518        })
1519        .unwrap();
1520        assert!(saw_unknown, "unknown key should still be reported");
1521        assert_eq!(
1522            body.as_deref(),
1523            Some("real"),
1524            "`body` must not be swallowed by a second skip"
1525        );
1526    }
1527}