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}