Skip to main content

aws_smithy_schema/schema/serde/
serializer.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Shape serialization interfaces for the Smithy data model.
7
8use super::error::SerdeError;
9use crate::Schema;
10use aws_smithy_types::{BigDecimal, BigInteger, DateTime, Document};
11
12/// Serializes Smithy shapes to a target format.
13///
14/// This trait provides a format-agnostic API for serializing the Smithy data model.
15/// Implementations serialize each data type to the corresponding encoding in their
16/// serial format (e.g., Smithy integers and floats to JSON numbers).
17///
18/// The serializer accepts a schema along with the value to provide additional
19/// information about how to serialize the value (e.g., timestamp format, JSON name).
20///
21/// This trait is object-safe so that generated `SerializableStruct` implementations
22/// can use `&mut dyn ShapeSerializer`, producing one compiled `serialize_members()`
23/// per shape regardless of how many codecs exist (`shapes + codecs` rather than
24/// `shapes * codecs` in binary size).
25///
26/// # Example
27///
28/// ```ignore
29/// let mut serializer = JsonSerializer::new();
30/// serializer.write_string(&STRING_SCHEMA, "hello")?;
31/// ```
32pub trait ShapeSerializer {
33    /// Writes a structure to the serializer.
34    ///
35    /// # Arguments
36    ///
37    /// * `schema` - The schema of the structure being serialized
38    /// * `value` - The structure to serialize
39    fn write_struct(
40        &mut self,
41        schema: &Schema,
42        value: &dyn SerializableStruct,
43    ) -> Result<(), SerdeError>;
44
45    /// Writes a list to the serializer.
46    ///
47    /// # Arguments
48    ///
49    /// * `schema` - The schema of the list being serialized
50    /// * `write_elements` - Callback that writes the list elements
51    fn write_list(
52        &mut self,
53        schema: &Schema,
54        write_elements: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
55    ) -> Result<(), SerdeError>;
56
57    /// Writes a map to the serializer.
58    ///
59    /// # Arguments
60    ///
61    /// * `schema` - The schema of the map being serialized
62    /// * `write_entries` - Callback that writes the map entries
63    fn write_map(
64        &mut self,
65        schema: &Schema,
66        write_entries: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
67    ) -> Result<(), SerdeError>;
68
69    /// Writes a boolean value.
70    fn write_boolean(&mut self, schema: &Schema, value: bool) -> Result<(), SerdeError>;
71
72    /// Writes a byte (i8) value.
73    fn write_byte(&mut self, schema: &Schema, value: i8) -> Result<(), SerdeError>;
74
75    /// Writes a short (i16) value.
76    fn write_short(&mut self, schema: &Schema, value: i16) -> Result<(), SerdeError>;
77
78    /// Writes an integer (i32) value.
79    fn write_integer(&mut self, schema: &Schema, value: i32) -> Result<(), SerdeError>;
80
81    /// Writes a long (i64) value.
82    fn write_long(&mut self, schema: &Schema, value: i64) -> Result<(), SerdeError>;
83
84    /// Writes a float (f32) value.
85    fn write_float(&mut self, schema: &Schema, value: f32) -> Result<(), SerdeError>;
86
87    /// Writes a double (f64) value.
88    fn write_double(&mut self, schema: &Schema, value: f64) -> Result<(), SerdeError>;
89
90    /// Writes a big integer value.
91    fn write_big_integer(&mut self, schema: &Schema, value: &BigInteger) -> Result<(), SerdeError>;
92
93    /// Writes a big decimal value.
94    fn write_big_decimal(&mut self, schema: &Schema, value: &BigDecimal) -> Result<(), SerdeError>;
95
96    /// Writes a string value.
97    fn write_string(&mut self, schema: &Schema, value: &str) -> Result<(), SerdeError>;
98
99    /// Writes a blob (byte array) value.
100    ///
101    /// Takes `&[u8]` rather than `&Blob` so callers that already hold byte
102    /// slices (e.g., a server holding a `bytes::Bytes` payload, or a codec
103    /// receiving payload bytes from elsewhere) don't need to wrap them in
104    /// a `Blob` just to satisfy the API. Generated client code sources
105    /// blobs from `Blob` data carriers and passes `blob.as_ref()`.
106    fn write_blob(&mut self, schema: &Schema, value: &[u8]) -> Result<(), SerdeError>;
107
108    /// Writes a timestamp value.
109    fn write_timestamp(&mut self, schema: &Schema, value: &DateTime) -> Result<(), SerdeError>;
110
111    /// Writes a document value.
112    fn write_document(&mut self, schema: &Schema, value: &Document) -> Result<(), SerdeError>;
113
114    /// Writes a null value (for sparse collections).
115    fn write_null(&mut self, schema: &Schema) -> Result<(), SerdeError>;
116
117    // --- Collection helper methods ---
118    //
119    // This is a **closed set** of helpers for the most common AWS collection
120    // patterns. No additional helpers will be added. New collection patterns
121    // should use the generic `write_list`/`write_map` with closures.
122    //
123    // These exist for two reasons:
124    // 1. Code size: each helper replaces ~6-8 lines of closure boilerplate in
125    //    generated code, yielding ~43% reduction for collection-heavy models.
126    // 2. Performance: the corresponding `ShapeDeserializer` helpers are
127    //    overridden by codec implementations (e.g., `JsonDeserializer`) to
128    //    avoid per-element vtable dispatch. Keeping them on the core trait
129    //    (rather than an extension trait) is required because they are called
130    //    through `&mut dyn ShapeSerializer`/`&mut dyn ShapeDeserializer` in
131    //    generated `serialize_members`/`deserialize` methods.
132
133    /// Writes a list of strings.
134    fn write_string_list(&mut self, schema: &Schema, values: &[String]) -> Result<(), SerdeError> {
135        self.write_list(schema, &|ser| {
136            for item in values {
137                ser.write_string(&crate::prelude::STRING, item)?;
138            }
139            Ok(())
140        })
141    }
142
143    /// Writes a list of blobs.
144    fn write_blob_list(
145        &mut self,
146        schema: &Schema,
147        values: &[aws_smithy_types::Blob],
148    ) -> Result<(), SerdeError> {
149        self.write_list(schema, &|ser| {
150            for item in values {
151                ser.write_blob(&crate::prelude::BLOB, item.as_ref())?;
152            }
153            Ok(())
154        })
155    }
156
157    /// Writes a list of integers.
158    fn write_integer_list(&mut self, schema: &Schema, values: &[i32]) -> Result<(), SerdeError> {
159        self.write_list(schema, &|ser| {
160            for item in values {
161                ser.write_integer(&crate::prelude::INTEGER, *item)?;
162            }
163            Ok(())
164        })
165    }
166
167    /// Writes a list of longs.
168    fn write_long_list(&mut self, schema: &Schema, values: &[i64]) -> Result<(), SerdeError> {
169        self.write_list(schema, &|ser| {
170            for item in values {
171                ser.write_long(&crate::prelude::LONG, *item)?;
172            }
173            Ok(())
174        })
175    }
176
177    /// Writes a map with string keys and string values.
178    fn write_string_string_map(
179        &mut self,
180        schema: &Schema,
181        values: &std::collections::HashMap<String, String>,
182    ) -> Result<(), SerdeError> {
183        self.write_map(schema, &|ser| {
184            for (key, value) in values {
185                ser.write_string(&crate::prelude::STRING, key)?;
186                ser.write_string(&crate::prelude::STRING, value)?;
187            }
188            Ok(())
189        })
190    }
191}
192
193/// Trait for structures that can be serialized via a schema.
194///
195/// Implemented by generated structure types. Because `ShapeSerializer` is object-safe,
196/// each struct gets one compiled `serialize_members()` that works with any serializer
197/// through dynamic dispatch.
198///
199/// # Example
200///
201/// ```ignore
202/// impl SerializableStruct for MyStruct {
203///     fn serialize_members(&self, serializer: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
204///         serializer.write_string(&NAME_SCHEMA, &self.name)?;
205///         serializer.write_integer(&AGE_SCHEMA, self.age)?;
206///         Ok(())
207///     }
208/// }
209/// ```
210pub trait SerializableStruct {
211    /// Serializes this structure's members using the provided serializer.
212    fn serialize_members(&self, serializer: &mut dyn ShapeSerializer) -> Result<(), SerdeError>;
213}
214
215impl<T: SerializableStruct + ?Sized> SerializableStruct for Box<T> {
216    fn serialize_members(&self, serializer: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
217        (**self).serialize_members(serializer)
218    }
219}