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}