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    /// Returns the size of the current container if known.
159    ///
160    /// This is an optimization hint that allows pre-allocating collections
161    /// with the correct capacity. Returns `None` if the size is unknown or
162    /// not applicable.
163    ///
164    /// Implementations SHOULD cap the returned value at a reasonable maximum
165    /// (e.g., 10,000) to prevent denial-of-service from untrusted payloads
166    /// that claim excessively large container sizes (e.g., a CBOR header
167    /// declaring billions of elements). Use [`capped_container_size`] to apply
168    /// a standard cap.
169    fn container_size(&self) -> Option<usize>;
170
171    // --- Collection helper methods ---
172    //
173    // This is a **closed set** of helpers for the most common AWS collection
174    // patterns. No additional helpers will be added. New collection patterns
175    // should use the generic `read_list`/`read_map` with closures.
176    //
177    // These exist for two reasons:
178    // 1. Code size: each helper replaces ~6-8 lines of closure boilerplate in
179    //    generated code, yielding ~43% reduction for collection-heavy models.
180    // 2. Performance: codec implementations (e.g., `JsonDeserializer`) override
181    //    these to call concrete `read_string`/`read_integer`/etc. methods
182    //    directly, eliminating per-element vtable dispatch. This requires the
183    //    methods to be on the core trait (not an extension trait) since they
184    //    are called through `&mut dyn ShapeDeserializer` in generated code.
185
186    /// Reads a list of strings.
187    fn read_string_list(&mut self, schema: &Schema<'_>) -> Result<Vec<String>, SerdeError> {
188        // Element reads receive the member (list element / map value) schema,
189        // not the container schema, so element-level traits (e.g.
190        // `@mediaType`, `@timestampFormat`) reach the codec — matching the
191        // serializer side. The prelude scalar is the fallback for a schema
192        // built without a member set.
193        let element = schema.member().unwrap_or(&crate::prelude::STRING);
194        let mut out = Vec::new();
195        self.read_list(schema, &mut |deser| {
196            out.push(deser.read_string(element)?);
197            Ok(())
198        })?;
199        Ok(out)
200    }
201
202    /// Reads a list of blobs.
203    fn read_blob_list(
204        &mut self,
205        schema: &Schema<'_>,
206    ) -> Result<Vec<aws_smithy_types::Blob>, SerdeError> {
207        let element = schema.member().unwrap_or(&crate::prelude::BLOB);
208        let mut out = Vec::new();
209        self.read_list(schema, &mut |deser| {
210            out.push(deser.read_blob(element)?);
211            Ok(())
212        })?;
213        Ok(out)
214    }
215
216    /// Reads a list of integers.
217    fn read_integer_list(&mut self, schema: &Schema<'_>) -> Result<Vec<i32>, SerdeError> {
218        let element = schema.member().unwrap_or(&crate::prelude::INTEGER);
219        let mut out = Vec::new();
220        self.read_list(schema, &mut |deser| {
221            out.push(deser.read_integer(element)?);
222            Ok(())
223        })?;
224        Ok(out)
225    }
226
227    /// Reads a list of longs.
228    fn read_long_list(&mut self, schema: &Schema<'_>) -> Result<Vec<i64>, SerdeError> {
229        let element = schema.member().unwrap_or(&crate::prelude::LONG);
230        let mut out = Vec::new();
231        self.read_list(schema, &mut |deser| {
232            out.push(deser.read_long(element)?);
233            Ok(())
234        })?;
235        Ok(out)
236    }
237
238    /// Reads a map with string values.
239    fn read_string_string_map(
240        &mut self,
241        schema: &Schema<'_>,
242    ) -> Result<std::collections::HashMap<String, String>, SerdeError> {
243        // `member()` returns the map *value* schema; the key is produced by
244        // `read_map` itself. Fall back to the prelude scalar when unset.
245        let value = schema.member().unwrap_or(&crate::prelude::STRING);
246        let mut out = std::collections::HashMap::new();
247        self.read_map(schema, &mut |key, deser| {
248            out.insert(key, deser.read_string(value)?);
249            Ok(())
250        })?;
251        Ok(out)
252    }
253}