Skip to main content

aws_smithy_schema/schema/serde/
deserializer.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Shape deserialization interfaces for the Smithy data model.
7
8use super::error::SerdeError;
9use crate::Schema;
10use aws_smithy_types::Document;
11use aws_smithy_types::{BigDecimal, BigInteger, Blob, DateTime};
12
13/// Deserializes Smithy shapes from a serial format.
14///
15/// This trait provides a format-agnostic API for deserializing the Smithy data model.
16/// Implementations read from a serial format and create data objects based on schemas.
17///
18/// The deserializer uses a consumer pattern for aggregate types (structures, lists, maps)
19/// to avoid trait object limitations and enable efficient deserialization without
20/// intermediate allocations.
21///
22/// # Consumer Pattern
23///
24/// For aggregate types, the deserializer calls a consumer function for each element/member.
25/// The consumer receives mutable state and updates it with each deserialized value.
26/// This pattern:
27/// - Avoids trait object issues with generic methods
28/// - Enables zero-cost abstractions (closures can be inlined)
29/// - Allows caller to control deserialization order and state management
30/// - Matches the SEP's recommendation for compiled typed languages
31/// - Uses `&mut dyn ShapeDeserializer` so composite deserializers (e.g., HTTP
32///   binding + body) can transparently delegate without the consumer knowing
33///   the concrete deserializer type. This enables runtime protocol swapping.
34///
35/// # Example
36///
37/// ```ignore
38/// // Deserializing a structure
39/// let mut builder = MyStructBuilder::default();
40/// deserializer.read_struct(
41///     &MY_STRUCT_SCHEMA,
42///     &mut |member, deser| {
43///         match member.member_index() {
44///             Some(0) => builder.field1 = Some(deser.read_string(member)?),
45///             Some(1) => builder.field2 = Some(deser.read_integer(member)?),
46///             _ => {}
47///         }
48///         Ok(())
49///     },
50/// )?;
51/// let my_struct = builder.build();
52/// ```
53/// Maximum pre-allocation size for containers, used to prevent denial-of-service
54/// from untrusted payloads claiming excessively large sizes.
55pub const MAX_CONTAINER_PREALLOC: usize = 10_000;
56
57/// Caps a raw container size at [`MAX_CONTAINER_PREALLOC`].
58///
59/// Implementations of [`ShapeDeserializer::container_size`] SHOULD use this
60/// when returning a size derived from untrusted input (e.g., a CBOR length header).
61pub fn capped_container_size(raw: usize) -> usize {
62    raw.min(MAX_CONTAINER_PREALLOC)
63}
64
65/// Reads values of the Smithy data model from a serialized source, guided by a
66/// [`Schema`].
67///
68/// This is the deserialization counterpart to
69/// [`ShapeSerializer`](crate::serde::ShapeSerializer): codecs implement it so a
70/// shape can be deserialized from any format without shape-specific code.
71pub trait ShapeDeserializer {
72    /// Reads a structure from the deserializer.
73    ///
74    /// The consumer is called for each member with the member schema and a
75    /// `&mut dyn ShapeDeserializer` to read the member value. Using `dyn`
76    /// allows composite deserializers (e.g., HTTP binding + body) to
77    /// transparently delegate without the consumer knowing the concrete type.
78    fn read_struct(
79        &mut self,
80        schema: &Schema<'_>,
81        state: &mut dyn FnMut(&Schema<'_>, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
82    ) -> Result<(), SerdeError>;
83
84    /// Reads a list from the deserializer.
85    ///
86    /// The consumer is called for each element with a `&mut dyn ShapeDeserializer`.
87    fn read_list(
88        &mut self,
89        schema: &Schema<'_>,
90        state: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
91    ) -> Result<(), SerdeError>;
92
93    /// Reads a map from the deserializer.
94    ///
95    /// The consumer is called for each entry with the key and a `&mut dyn ShapeDeserializer`.
96    fn read_map(
97        &mut self,
98        schema: &Schema<'_>,
99        state: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
100    ) -> Result<(), SerdeError>;
101
102    /// Reads a boolean value.
103    fn read_boolean(&mut self, schema: &Schema<'_>) -> Result<bool, SerdeError>;
104
105    /// Reads a byte (i8) value.
106    fn read_byte(&mut self, schema: &Schema<'_>) -> Result<i8, SerdeError>;
107
108    /// Reads a short (i16) value.
109    fn read_short(&mut self, schema: &Schema<'_>) -> Result<i16, SerdeError>;
110
111    /// Reads an integer (i32) value.
112    fn read_integer(&mut self, schema: &Schema<'_>) -> Result<i32, SerdeError>;
113
114    /// Reads a long (i64) value.
115    fn read_long(&mut self, schema: &Schema<'_>) -> Result<i64, SerdeError>;
116
117    /// Reads a float (f32) value.
118    fn read_float(&mut self, schema: &Schema<'_>) -> Result<f32, SerdeError>;
119
120    /// Reads a double (f64) value.
121    fn read_double(&mut self, schema: &Schema<'_>) -> Result<f64, SerdeError>;
122
123    /// Reads a big integer value.
124    fn read_big_integer(&mut self, schema: &Schema<'_>) -> Result<BigInteger, SerdeError>;
125
126    /// Reads a big decimal value.
127    fn read_big_decimal(&mut self, schema: &Schema<'_>) -> Result<BigDecimal, SerdeError>;
128
129    /// Reads a string value.
130    fn read_string(&mut self, schema: &Schema<'_>) -> Result<String, SerdeError>;
131
132    /// Reads a blob (byte array) value.
133    fn read_blob(&mut self, schema: &Schema<'_>) -> Result<Blob, SerdeError>;
134
135    /// Reads a timestamp value.
136    fn read_timestamp(&mut self, schema: &Schema<'_>) -> Result<DateTime, SerdeError>;
137
138    /// Reads a document value.
139    ///
140    /// Returns the [`aws_smithy_types::Document`] (fully owned,
141    /// no lifetime). Implementations construct the value from their
142    /// underlying source representation.
143    fn read_document(&mut self, schema: &Schema<'_>) -> Result<Document, SerdeError>;
144
145    /// Checks if the current value is null.
146    ///
147    /// This is used for sparse collections where null values are significant.
148    fn is_null(&self) -> bool;
149
150    /// Consumes a null value, advancing past it.
151    ///
152    /// This should be called after `is_null()` returns true to advance the
153    /// deserializer past the null token.
154    fn read_null(&mut self) -> Result<(), SerdeError> {
155        Ok(())
156    }
157
158    /// Consumes the current value without interpreting it.
159    ///
160    /// A consumer calls this when a value is present but must not be used. The
161    /// motivating case is a protocol body that carries a field for a member whose
162    /// authoritative value comes from elsewhere in the message — for example an HTTP
163    /// response where the member is bound to a header or the status code. The
164    /// transport value wins, so the body copy is consumed and discarded rather than
165    /// being allowed to overwrite it.
166    ///
167    /// # Implementor contract
168    ///
169    /// The requirement is that **after this returns, the parent aggregate read can
170    /// continue correctly**. What that takes depends on how the implementation hands
171    /// values to a consumer, and the two cases are opposites:
172    ///
173    /// - **Cursor-based** formats (JSON, CBOR) position a single shared cursor at the
174    ///   value and rely on the consumer to move it. These MUST advance past the
175    ///   complete value, including all nested content. Failing to advance leaves the
176    ///   cursor mid-value and corrupts the rest of the parse.
177    /// - **Pre-isolated** formats (XML, document trees, and single-value adapters)
178    ///   give the consumer a value the parent has already delimited, and the parent's
179    ///   own iteration advances independently. These MUST NOT attempt a second
180    ///   advance, and so return `Ok(())` without doing anything.
181    ///
182    /// The default is a correctness fallback for third-party codecs that have not
183    /// considered this method: it reads and drops a [`Document`], which advances a
184    /// cursor-based codec correctly at the cost of an allocation. Implementations
185    /// SHOULD override it. A codec that cannot produce documents will surface
186    /// [`SerdeError::unsupported`] from the default, which is why the built-in
187    /// deserializers all override it and never allocate here.
188    fn skip_value(&mut self) -> Result<(), SerdeError> {
189        let _ = self.read_document(&crate::prelude::DOCUMENT)?;
190        Ok(())
191    }
192
193    /// Returns the size of the current container if known.
194    ///
195    /// This is an optimization hint that allows pre-allocating collections
196    /// with the correct capacity. Returns `None` if the size is unknown or
197    /// not applicable.
198    ///
199    /// Implementations SHOULD cap the returned value at a reasonable maximum
200    /// (e.g., 10,000) to prevent denial-of-service from untrusted payloads
201    /// that claim excessively large container sizes (e.g., a CBOR header
202    /// declaring billions of elements). Use [`capped_container_size`] to apply
203    /// a standard cap.
204    fn container_size(&self) -> Option<usize>;
205
206    // --- Collection helper methods ---
207    //
208    // This is a **closed set** of helpers for the most common AWS collection
209    // patterns. No additional helpers will be added. New collection patterns
210    // should use the generic `read_list`/`read_map` with closures.
211    //
212    // These exist for two reasons:
213    // 1. Code size: each helper replaces ~6-8 lines of closure boilerplate in
214    //    generated code, yielding ~43% reduction for collection-heavy models.
215    // 2. Performance: codec implementations (e.g., `JsonDeserializer`) override
216    //    these to call concrete `read_string`/`read_integer`/etc. methods
217    //    directly, eliminating per-element vtable dispatch. This requires the
218    //    methods to be on the core trait (not an extension trait) since they
219    //    are called through `&mut dyn ShapeDeserializer` in generated code.
220
221    /// Reads a list of strings.
222    fn read_string_list(&mut self, schema: &Schema<'_>) -> Result<Vec<String>, SerdeError> {
223        // Element reads receive the member (list element / map value) schema,
224        // not the container schema, so element-level traits (e.g.
225        // `@mediaType`, `@timestampFormat`) reach the codec — matching the
226        // serializer side. The prelude scalar is the fallback for a schema
227        // built without a member set.
228        let element = schema.member().unwrap_or(&crate::prelude::STRING);
229        let mut out = Vec::new();
230        self.read_list(schema, &mut |deser| {
231            out.push(deser.read_string(element)?);
232            Ok(())
233        })?;
234        Ok(out)
235    }
236
237    /// Reads a list of blobs.
238    fn read_blob_list(
239        &mut self,
240        schema: &Schema<'_>,
241    ) -> Result<Vec<aws_smithy_types::Blob>, SerdeError> {
242        let element = schema.member().unwrap_or(&crate::prelude::BLOB);
243        let mut out = Vec::new();
244        self.read_list(schema, &mut |deser| {
245            out.push(deser.read_blob(element)?);
246            Ok(())
247        })?;
248        Ok(out)
249    }
250
251    /// Reads a list of integers.
252    fn read_integer_list(&mut self, schema: &Schema<'_>) -> Result<Vec<i32>, SerdeError> {
253        let element = schema.member().unwrap_or(&crate::prelude::INTEGER);
254        let mut out = Vec::new();
255        self.read_list(schema, &mut |deser| {
256            out.push(deser.read_integer(element)?);
257            Ok(())
258        })?;
259        Ok(out)
260    }
261
262    /// Reads a list of longs.
263    fn read_long_list(&mut self, schema: &Schema<'_>) -> Result<Vec<i64>, SerdeError> {
264        let element = schema.member().unwrap_or(&crate::prelude::LONG);
265        let mut out = Vec::new();
266        self.read_list(schema, &mut |deser| {
267            out.push(deser.read_long(element)?);
268            Ok(())
269        })?;
270        Ok(out)
271    }
272
273    /// Reads a map with string values.
274    fn read_string_string_map(
275        &mut self,
276        schema: &Schema<'_>,
277    ) -> Result<std::collections::HashMap<String, String>, SerdeError> {
278        // `member()` returns the map *value* schema; the key is produced by
279        // `read_map` itself. Fall back to the prelude scalar when unset.
280        let value = schema.member().unwrap_or(&crate::prelude::STRING);
281        let mut out = std::collections::HashMap::new();
282        self.read_map(schema, &mut |key, deser| {
283            out.insert(key, deser.read_string(value)?);
284            Ok(())
285        })?;
286        Ok(out)
287    }
288}
289
290/// Tests for the defaulted [`ShapeDeserializer::skip_value`].
291///
292/// The default exists so that a third-party codec written before this method compiles
293/// unchanged and still behaves correctly. These lock in what "correctly" means: a codec
294/// that can produce documents gets a working (if allocating) skip, and one that cannot
295/// gets a clear error rather than silent cursor corruption.
296#[cfg(test)]
297mod default_skip_value {
298    use super::*;
299    use crate::{shape_id, ShapeType};
300
301    /// A minimal third-party-style deserializer that does **not** override `skip_value`.
302    /// `read_document` is the only method that does anything, mirroring a codec whose
303    /// document support is its generic value reader.
304    struct DocumentCapable {
305        read_documents: usize,
306    }
307
308    impl ShapeDeserializer for DocumentCapable {
309        fn read_struct(
310            &mut self,
311            _schema: &Schema<'_>,
312            _consumer: &mut dyn FnMut(
313                &Schema<'_>,
314                &mut dyn ShapeDeserializer,
315            ) -> Result<(), SerdeError>,
316        ) -> Result<(), SerdeError> {
317            Ok(())
318        }
319        fn read_list(
320            &mut self,
321            _schema: &Schema<'_>,
322            _consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
323        ) -> Result<(), SerdeError> {
324            Ok(())
325        }
326        fn read_map(
327            &mut self,
328            _schema: &Schema<'_>,
329            _consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
330        ) -> Result<(), SerdeError> {
331            Ok(())
332        }
333        fn read_boolean(&mut self, _schema: &Schema<'_>) -> Result<bool, SerdeError> {
334            Ok(false)
335        }
336        fn read_byte(&mut self, _schema: &Schema<'_>) -> Result<i8, SerdeError> {
337            Ok(0)
338        }
339        fn read_short(&mut self, _schema: &Schema<'_>) -> Result<i16, SerdeError> {
340            Ok(0)
341        }
342        fn read_integer(&mut self, _schema: &Schema<'_>) -> Result<i32, SerdeError> {
343            Ok(0)
344        }
345        fn read_long(&mut self, _schema: &Schema<'_>) -> Result<i64, SerdeError> {
346            Ok(0)
347        }
348        fn read_float(&mut self, _schema: &Schema<'_>) -> Result<f32, SerdeError> {
349            Ok(0.0)
350        }
351        fn read_double(&mut self, _schema: &Schema<'_>) -> Result<f64, SerdeError> {
352            Ok(0.0)
353        }
354        fn read_big_integer(&mut self, _schema: &Schema<'_>) -> Result<BigInteger, SerdeError> {
355            Err(SerdeError::unsupported("big integer"))
356        }
357        fn read_big_decimal(&mut self, _schema: &Schema<'_>) -> Result<BigDecimal, SerdeError> {
358            Err(SerdeError::unsupported("big decimal"))
359        }
360        fn read_string(&mut self, _schema: &Schema<'_>) -> Result<String, SerdeError> {
361            Ok(String::new())
362        }
363        fn read_blob(&mut self, _schema: &Schema<'_>) -> Result<Blob, SerdeError> {
364            Ok(Blob::new(Vec::new()))
365        }
366        fn read_timestamp(&mut self, _schema: &Schema<'_>) -> Result<DateTime, SerdeError> {
367            Ok(DateTime::from_secs(0))
368        }
369        fn read_document(&mut self, _schema: &Schema<'_>) -> Result<Document, SerdeError> {
370            self.read_documents += 1;
371            Ok(Document::Null)
372        }
373        fn is_null(&self) -> bool {
374            false
375        }
376        fn container_size(&self) -> Option<usize> {
377            None
378        }
379    }
380
381    /// Same, but cannot produce documents — the case the doc comment warns about.
382    struct NotDocumentCapable(DocumentCapable);
383
384    impl ShapeDeserializer for NotDocumentCapable {
385        fn read_struct(
386            &mut self,
387            schema: &Schema<'_>,
388            consumer: &mut dyn FnMut(
389                &Schema<'_>,
390                &mut dyn ShapeDeserializer,
391            ) -> Result<(), SerdeError>,
392        ) -> Result<(), SerdeError> {
393            self.0.read_struct(schema, consumer)
394        }
395        fn read_list(
396            &mut self,
397            schema: &Schema<'_>,
398            consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
399        ) -> Result<(), SerdeError> {
400            self.0.read_list(schema, consumer)
401        }
402        fn read_map(
403            &mut self,
404            schema: &Schema<'_>,
405            consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
406        ) -> Result<(), SerdeError> {
407            self.0.read_map(schema, consumer)
408        }
409        fn read_boolean(&mut self, s: &Schema<'_>) -> Result<bool, SerdeError> {
410            self.0.read_boolean(s)
411        }
412        fn read_byte(&mut self, s: &Schema<'_>) -> Result<i8, SerdeError> {
413            self.0.read_byte(s)
414        }
415        fn read_short(&mut self, s: &Schema<'_>) -> Result<i16, SerdeError> {
416            self.0.read_short(s)
417        }
418        fn read_integer(&mut self, s: &Schema<'_>) -> Result<i32, SerdeError> {
419            self.0.read_integer(s)
420        }
421        fn read_long(&mut self, s: &Schema<'_>) -> Result<i64, SerdeError> {
422            self.0.read_long(s)
423        }
424        fn read_float(&mut self, s: &Schema<'_>) -> Result<f32, SerdeError> {
425            self.0.read_float(s)
426        }
427        fn read_double(&mut self, s: &Schema<'_>) -> Result<f64, SerdeError> {
428            self.0.read_double(s)
429        }
430        fn read_big_integer(&mut self, s: &Schema<'_>) -> Result<BigInteger, SerdeError> {
431            self.0.read_big_integer(s)
432        }
433        fn read_big_decimal(&mut self, s: &Schema<'_>) -> Result<BigDecimal, SerdeError> {
434            self.0.read_big_decimal(s)
435        }
436        fn read_string(&mut self, s: &Schema<'_>) -> Result<String, SerdeError> {
437            self.0.read_string(s)
438        }
439        fn read_blob(&mut self, s: &Schema<'_>) -> Result<Blob, SerdeError> {
440            self.0.read_blob(s)
441        }
442        fn read_timestamp(&mut self, s: &Schema<'_>) -> Result<DateTime, SerdeError> {
443            self.0.read_timestamp(s)
444        }
445        fn read_document(&mut self, _schema: &Schema<'_>) -> Result<Document, SerdeError> {
446            Err(SerdeError::unsupported(
447                "this codec has no document support",
448            ))
449        }
450        fn is_null(&self) -> bool {
451            false
452        }
453        fn container_size(&self) -> Option<usize> {
454            None
455        }
456    }
457
458    #[test]
459    fn default_consumes_the_value_through_read_document() {
460        let mut deser = DocumentCapable { read_documents: 0 };
461        deser.skip_value().expect("default should succeed");
462        assert_eq!(
463            deser.read_documents, 1,
464            "the default is specified to consume the value via `read_document`"
465        );
466    }
467
468    #[test]
469    fn default_passes_the_prelude_document_schema() {
470        // Any codec keying behavior off the schema must receive the prelude document
471        // schema, not a member schema it would try to interpret.
472        struct CaptureSchema(Option<(String, String)>);
473        impl ShapeDeserializer for CaptureSchema {
474            fn read_struct(
475                &mut self,
476                _s: &Schema<'_>,
477                _c: &mut dyn FnMut(
478                    &Schema<'_>,
479                    &mut dyn ShapeDeserializer,
480                ) -> Result<(), SerdeError>,
481            ) -> Result<(), SerdeError> {
482                Ok(())
483            }
484            fn read_list(
485                &mut self,
486                _s: &Schema<'_>,
487                _c: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
488            ) -> Result<(), SerdeError> {
489                Ok(())
490            }
491            fn read_map(
492                &mut self,
493                _s: &Schema<'_>,
494                _c: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
495            ) -> Result<(), SerdeError> {
496                Ok(())
497            }
498            fn read_boolean(&mut self, _s: &Schema<'_>) -> Result<bool, SerdeError> {
499                Ok(false)
500            }
501            fn read_byte(&mut self, _s: &Schema<'_>) -> Result<i8, SerdeError> {
502                Ok(0)
503            }
504            fn read_short(&mut self, _s: &Schema<'_>) -> Result<i16, SerdeError> {
505                Ok(0)
506            }
507            fn read_integer(&mut self, _s: &Schema<'_>) -> Result<i32, SerdeError> {
508                Ok(0)
509            }
510            fn read_long(&mut self, _s: &Schema<'_>) -> Result<i64, SerdeError> {
511                Ok(0)
512            }
513            fn read_float(&mut self, _s: &Schema<'_>) -> Result<f32, SerdeError> {
514                Ok(0.0)
515            }
516            fn read_double(&mut self, _s: &Schema<'_>) -> Result<f64, SerdeError> {
517                Ok(0.0)
518            }
519            fn read_big_integer(&mut self, _s: &Schema<'_>) -> Result<BigInteger, SerdeError> {
520                Err(SerdeError::unsupported("x"))
521            }
522            fn read_big_decimal(&mut self, _s: &Schema<'_>) -> Result<BigDecimal, SerdeError> {
523                Err(SerdeError::unsupported("x"))
524            }
525            fn read_string(&mut self, _s: &Schema<'_>) -> Result<String, SerdeError> {
526                Ok(String::new())
527            }
528            fn read_blob(&mut self, _s: &Schema<'_>) -> Result<Blob, SerdeError> {
529                Ok(Blob::new(Vec::new()))
530            }
531            fn read_timestamp(&mut self, _s: &Schema<'_>) -> Result<DateTime, SerdeError> {
532                Ok(DateTime::from_secs(0))
533            }
534            fn read_document(&mut self, schema: &Schema<'_>) -> Result<Document, SerdeError> {
535                let id = schema.shape_id();
536                self.0 = Some((id.namespace().to_owned(), id.shape_name().to_owned()));
537                Ok(Document::Null)
538            }
539            fn is_null(&self) -> bool {
540                false
541            }
542            fn container_size(&self) -> Option<usize> {
543                None
544            }
545        }
546
547        let mut deser = CaptureSchema(None);
548        deser.skip_value().unwrap();
549        let want = crate::prelude::DOCUMENT.shape_id();
550        assert_eq!(
551            deser.0,
552            Some((want.namespace().to_owned(), want.shape_name().to_owned())),
553            "the default must pass the prelude document schema"
554        );
555    }
556
557    #[test]
558    fn default_surfaces_unsupported_for_a_codec_without_documents() {
559        // Reporting an error is the intended outcome: it is strictly better than
560        // pretending the value was consumed and corrupting the rest of the parse.
561        let mut deser = NotDocumentCapable(DocumentCapable { read_documents: 0 });
562        let err = deser.skip_value().expect_err("should not silently succeed");
563        assert!(
564            err.to_string().contains("document support"),
565            "unexpected error: {err}"
566        );
567    }
568
569    #[test]
570    fn skip_value_is_object_safe() {
571        // The composite calls this through `&mut dyn ShapeDeserializer`, so the method
572        // must remain dispatchable on a trait object.
573        static S: Schema<'static> =
574            Schema::new_member(shape_id!("test", "S"), ShapeType::String, "v", 0);
575        let _ = &S;
576        let mut concrete = DocumentCapable { read_documents: 0 };
577        let dynamic: &mut dyn ShapeDeserializer = &mut concrete;
578        dynamic.skip_value().unwrap();
579        assert_eq!(concrete.read_documents, 1);
580    }
581}