Skip to main content

aws_smithy_schema/schema/
codec.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Codec trait for creating shape serializers and deserializers.
7//!
8//! A codec represents a specific serialization format (e.g., JSON, XML, CBOR)
9//! and provides methods to create serializers and deserializers for that format.
10
11pub mod http_string;
12
13use crate::serde::{ShapeDeserializer, ShapeSerializer};
14
15/// Trait for serializers that can produce a final byte output.
16///
17/// This is separate from [`ShapeSerializer`] to preserve object safety on
18/// [`ShapeSerializer`] (which is used as `&mut dyn ShapeSerializer` in generated code).
19///
20/// # Why isn't `FinishSerializer` itself object-safe?
21///
22/// [`FinishSerializer::finish`] takes `self` by value so it can consume and tear down the
23/// serializer (e.g., return an owned `Vec<u8>` without a leftover borrow on the serializer's
24/// internal buffer). Methods that receive `self` by value are not dispatchable through a
25/// trait object: `dyn FinishSerializer` doesn't know the concrete size of `Self`, so it
26/// cannot move it. This is the standard Rust object-safety restriction.
27///
28/// The consequence is that `FinishSerializer` can only be used with a statically-known
29/// serializer type, which is fine for generated code that knows the concrete [`Codec`].
30/// For call sites that need dynamic dispatch (e.g., event stream marshallers that receive
31/// a `Box<dyn PayloadSerializer>` from `ClientProtocol::payload_codec`), use
32/// [`PayloadSerializer::finish_boxed`] instead — it takes `self: Box<Self>`, which *is*
33/// object-safe because the `Box` owns the value and knows how to drop it.
34pub trait FinishSerializer {
35    /// Consumes the serializer and returns the serialized bytes.
36    fn finish(self) -> Vec<u8>;
37}
38
39/// A codec for a specific serialization format.
40///
41/// Codecs are responsible for creating [`ShapeSerializer`] and [`ShapeDeserializer`]
42/// instances that can serialize and deserialize shapes to and from a specific format.
43///
44/// # Examples
45///
46/// Implementing a custom codec:
47///
48/// ```ignore
49/// use aws_smithy_schema::codec::Codec;
50/// use aws_smithy_schema::serde::{ShapeSerializer, ShapeDeserializer};
51///
52/// struct MyCodec {
53///     // codec configuration
54/// }
55///
56/// impl Codec for MyCodec {
57///     type Serializer = MySerializer;
58///     type Deserializer = MyDeserializer;
59///
60///     fn create_serializer(&self) -> Self::Serializer {
61///         MySerializer::new()
62///     }
63///
64///     fn create_deserializer(&self, input: &[u8]) -> Self::Deserializer {
65///         MyDeserializer::new(input)
66///     }
67/// }
68/// ```
69pub trait Codec {
70    /// The serializer type for this codec.
71    type Serializer: ShapeSerializer + FinishSerializer;
72
73    /// The deserializer type for this codec.
74    type Deserializer<'a>: ShapeDeserializer + 'a;
75
76    /// Creates a new serializer for this codec.
77    fn create_serializer(&self) -> Self::Serializer;
78
79    /// Creates a new deserializer for this codec from the given input bytes.
80    fn create_deserializer<'a>(&self, input: &'a [u8]) -> Self::Deserializer<'a>;
81}
82
83/// Object-safe view of a codec's serializer.
84///
85/// Combines [`ShapeSerializer`] with an object-safe finish operation. [`FinishSerializer::finish`]
86/// takes `self` by value, which cannot be called through `&mut dyn ShapeSerializer` — so this
87/// trait exposes `finish(self: Box<Self>)` instead, which is object-safe.
88///
89/// A blanket impl is provided for every `ShapeSerializer + FinishSerializer`, so every concrete
90/// codec serializer (e.g., `JsonSerializer`) is automatically usable through `Box<dyn PayloadSerializer>`
91/// without requiring codec authors to write extra impls.
92pub trait PayloadSerializer: ShapeSerializer {
93    /// Consumes this boxed serializer and returns the serialized bytes.
94    fn finish_boxed(self: Box<Self>) -> Vec<u8>;
95}
96
97impl<S> PayloadSerializer for S
98where
99    S: ShapeSerializer + FinishSerializer,
100{
101    fn finish_boxed(self: Box<Self>) -> Vec<u8> {
102        <S as FinishSerializer>::finish(*self)
103    }
104}
105
106/// Object-safe sibling of [`Codec`] exposing dynamic deserializer creation.
107///
108/// # Why a sibling trait?
109///
110/// [`Codec`] uses associated types (`Serializer`, `Deserializer<'a>`) and
111/// returns them by value from its methods. This gives codec consumers
112/// zero-cost static dispatch — the compiler can inline and monomorphize
113/// serializer/deserializer creation at call sites that know the concrete
114/// codec type. That is the right choice for the common case (generated code
115/// that knows the protocol statically).
116///
117/// However, some features require accessing a codec through a trait object.
118/// The SEP-specified `ClientProtocol::payload_codec()` method returns "the
119/// codec" in a context where the `ClientProtocol` itself is accessed via
120/// `dyn ClientProtocol` (see [`SharedClientProtocol`](crate::protocol::SharedClientProtocol),
121/// which stores `Arc<dyn ClientProtocol>` for runtime protocol swapping).
122/// Returning a [`Codec`] through `dyn` is not possible in Rust because
123/// associated types and by-value returns are not object-safe.
124///
125/// `DynCodec` is the minimal object-safe view that covers the operations
126/// needed through a trait object. It exists purely as a Rust adaptation of
127/// the SEP's object-oriented `Codec` design; it is not additional user-facing
128/// API. A blanket `impl<C: Codec> DynCodec for C` makes every concrete codec
129/// automatically usable through `&dyn DynCodec` without any extra work from
130/// codec authors.
131///
132/// Both deserializer and serializer creation are exposed through this trait
133/// to support event-stream marshalling (input streams) and unmarshalling
134/// (output streams) through `dyn ClientProtocol`.
135///
136/// # Returning (de)serializers as boxed trait objects
137///
138/// [`ShapeDeserializer`] implementations typically hold cursor state over an
139/// input byte slice (e.g., `JsonDeserializer` holds `input: &'a [u8]` and a
140/// `position: usize`). Producing a fresh deserializer positioned at the start
141/// of the input is the standard way to read independent messages — as is
142/// required for event-stream frames, where each frame is an independent
143/// serialized payload. The returned `Box<dyn ShapeDeserializer + 'a>`
144/// borrows from `input`, so the caller retains ownership of the bytes for
145/// the duration of deserialization. Similarly each event frame requires a
146/// fresh serializer.
147pub trait DynCodec: Send + Sync + std::fmt::Debug {
148    /// Creates a new deserializer over the given input bytes.
149    fn create_deserializer<'a>(&self, input: &'a [u8]) -> Box<dyn ShapeDeserializer + 'a>;
150
151    /// Creates a new serializer. Use [`PayloadSerializer::finish_boxed`] to
152    /// consume the serializer and obtain the serialized bytes.
153    fn create_serializer(&self) -> Box<dyn PayloadSerializer + '_>;
154}
155
156// Blanket implementation: any statically-dispatched `Codec` is automatically
157// available as a `DynCodec`. The boxed (de)serializer here incurs one heap
158// allocation per call, which is acceptable for the per-event-frame use case.
159// Callers using `Codec` directly pay no such cost.
160impl<C> DynCodec for C
161where
162    C: Codec + Send + Sync + std::fmt::Debug,
163{
164    fn create_deserializer<'a>(&self, input: &'a [u8]) -> Box<dyn ShapeDeserializer + 'a> {
165        Box::new(<C as Codec>::create_deserializer(self, input))
166    }
167
168    fn create_serializer(&self) -> Box<dyn PayloadSerializer + '_> {
169        Box::new(<C as Codec>::create_serializer(self))
170    }
171}
172
173#[cfg(test)]
174mod test {
175    use super::*;
176    use crate::serde::{SerdeError, SerializableStruct, ShapeDeserializer, ShapeSerializer};
177    use crate::{prelude::*, Schema};
178
179    // Mock serializer
180    struct MockSerializer {
181        output: Vec<u8>,
182    }
183
184    impl MockSerializer {
185        fn finish(self) -> Vec<u8> {
186            self.output
187        }
188    }
189
190    impl FinishSerializer for MockSerializer {
191        fn finish(self) -> Vec<u8> {
192            self.output
193        }
194    }
195
196    impl ShapeSerializer for MockSerializer {
197        fn write_struct(
198            &mut self,
199            _schema: &Schema,
200            _value: &dyn SerializableStruct,
201        ) -> Result<(), SerdeError> {
202            Ok(())
203        }
204
205        fn write_list(
206            &mut self,
207            _schema: &Schema,
208            _write_elements: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
209        ) -> Result<(), SerdeError> {
210            Ok(())
211        }
212
213        fn write_map(
214            &mut self,
215            _schema: &Schema,
216            _write_entries: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
217        ) -> Result<(), SerdeError> {
218            Ok(())
219        }
220
221        fn write_boolean(&mut self, _schema: &Schema, _value: bool) -> Result<(), SerdeError> {
222            Ok(())
223        }
224
225        fn write_byte(&mut self, _schema: &Schema, _value: i8) -> Result<(), SerdeError> {
226            Ok(())
227        }
228
229        fn write_short(&mut self, _schema: &Schema, _value: i16) -> Result<(), SerdeError> {
230            Ok(())
231        }
232
233        fn write_integer(&mut self, _schema: &Schema, _value: i32) -> Result<(), SerdeError> {
234            Ok(())
235        }
236
237        fn write_long(&mut self, _schema: &Schema, _value: i64) -> Result<(), SerdeError> {
238            Ok(())
239        }
240
241        fn write_float(&mut self, _schema: &Schema, _value: f32) -> Result<(), SerdeError> {
242            Ok(())
243        }
244
245        fn write_double(&mut self, _schema: &Schema, _value: f64) -> Result<(), SerdeError> {
246            Ok(())
247        }
248
249        fn write_big_integer(
250            &mut self,
251            _schema: &Schema,
252            _value: &aws_smithy_types::BigInteger,
253        ) -> Result<(), SerdeError> {
254            Ok(())
255        }
256
257        fn write_big_decimal(
258            &mut self,
259            _schema: &Schema,
260            _value: &aws_smithy_types::BigDecimal,
261        ) -> Result<(), SerdeError> {
262            Ok(())
263        }
264
265        fn write_string(&mut self, _schema: &Schema, _value: &str) -> Result<(), SerdeError> {
266            Ok(())
267        }
268
269        fn write_blob(&mut self, _schema: &Schema, _value: &[u8]) -> Result<(), SerdeError> {
270            Ok(())
271        }
272
273        fn write_timestamp(
274            &mut self,
275            _schema: &Schema,
276            _value: &aws_smithy_types::DateTime,
277        ) -> Result<(), SerdeError> {
278            Ok(())
279        }
280
281        fn write_document(
282            &mut self,
283            _schema: &Schema,
284            _value: &aws_smithy_types::Document,
285        ) -> Result<(), SerdeError> {
286            Ok(())
287        }
288
289        fn write_null(&mut self, _schema: &Schema) -> Result<(), SerdeError> {
290            Ok(())
291        }
292    }
293
294    // Mock deserializer
295    struct MockDeserializer<'a> {
296        #[allow(dead_code)]
297        input: &'a [u8],
298    }
299
300    impl<'a> ShapeDeserializer for MockDeserializer<'a> {
301        fn read_struct(
302            &mut self,
303            _schema: &Schema,
304            _consumer: &mut dyn FnMut(
305                &Schema,
306                &mut dyn ShapeDeserializer,
307            ) -> Result<(), SerdeError>,
308        ) -> Result<(), SerdeError> {
309            Ok(())
310        }
311
312        fn read_list(
313            &mut self,
314            _schema: &Schema,
315            _consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
316        ) -> Result<(), SerdeError> {
317            Ok(())
318        }
319
320        fn read_map(
321            &mut self,
322            _schema: &Schema,
323            _consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
324        ) -> Result<(), SerdeError> {
325            Ok(())
326        }
327
328        fn read_boolean(&mut self, _schema: &Schema) -> Result<bool, SerdeError> {
329            Ok(false)
330        }
331
332        fn read_byte(&mut self, _schema: &Schema) -> Result<i8, SerdeError> {
333            Ok(0)
334        }
335
336        fn read_short(&mut self, _schema: &Schema) -> Result<i16, SerdeError> {
337            Ok(0)
338        }
339
340        fn read_integer(&mut self, _schema: &Schema) -> Result<i32, SerdeError> {
341            Ok(0)
342        }
343
344        fn read_long(&mut self, _schema: &Schema) -> Result<i64, SerdeError> {
345            Ok(0)
346        }
347
348        fn read_float(&mut self, _schema: &Schema) -> Result<f32, SerdeError> {
349            Ok(0.0)
350        }
351
352        fn read_double(&mut self, _schema: &Schema) -> Result<f64, SerdeError> {
353            Ok(0.0)
354        }
355
356        fn read_big_integer(
357            &mut self,
358            _schema: &Schema,
359        ) -> Result<aws_smithy_types::BigInteger, SerdeError> {
360            use std::str::FromStr;
361            Ok(aws_smithy_types::BigInteger::from_str("0").unwrap())
362        }
363
364        fn read_big_decimal(
365            &mut self,
366            _schema: &Schema,
367        ) -> Result<aws_smithy_types::BigDecimal, SerdeError> {
368            use std::str::FromStr;
369            Ok(aws_smithy_types::BigDecimal::from_str("0").unwrap())
370        }
371
372        fn read_string(&mut self, _schema: &Schema) -> Result<String, SerdeError> {
373            Ok(String::new())
374        }
375
376        fn read_blob(&mut self, _schema: &Schema) -> Result<aws_smithy_types::Blob, SerdeError> {
377            Ok(aws_smithy_types::Blob::new(Vec::new()))
378        }
379
380        fn read_timestamp(
381            &mut self,
382            _schema: &Schema,
383        ) -> Result<aws_smithy_types::DateTime, SerdeError> {
384            Ok(aws_smithy_types::DateTime::from_secs(0))
385        }
386
387        fn read_document(
388            &mut self,
389            _schema: &Schema,
390        ) -> Result<aws_smithy_types::Document, SerdeError> {
391            Ok(aws_smithy_types::Document::Null)
392        }
393
394        fn is_null(&self) -> bool {
395            false
396        }
397
398        fn container_size(&self) -> Option<usize> {
399            None
400        }
401    }
402
403    // Mock codec
404    struct MockCodec;
405
406    impl Codec for MockCodec {
407        type Serializer = MockSerializer;
408        type Deserializer<'a> = MockDeserializer<'a>;
409
410        fn create_serializer(&self) -> Self::Serializer {
411            MockSerializer { output: Vec::new() }
412        }
413
414        fn create_deserializer<'a>(&self, input: &'a [u8]) -> Self::Deserializer<'a> {
415            MockDeserializer { input }
416        }
417    }
418
419    #[test]
420    fn test_codec_create_serializer() {
421        let codec = MockCodec;
422        let mut serializer = codec.create_serializer();
423
424        // Test that we can use the serializer
425        serializer.write_string(&STRING, "test").unwrap();
426        let output = serializer.finish();
427        assert_eq!(output, Vec::<u8>::new());
428    }
429
430    #[test]
431    fn test_codec_create_deserializer() {
432        let codec = MockCodec;
433        let input = b"test data";
434        let mut deserializer = codec.create_deserializer(input);
435
436        // Test that we can use the deserializer
437        let result = deserializer.read_string(&STRING).unwrap();
438        assert_eq!(result, "");
439    }
440
441    #[test]
442    fn test_codec_roundtrip() {
443        let codec = MockCodec;
444
445        // Serialize
446        let mut serializer = codec.create_serializer();
447        serializer.write_integer(&INTEGER, 42).unwrap();
448        let bytes = serializer.finish();
449
450        // Deserialize
451        let mut deserializer = codec.create_deserializer(&bytes);
452        let value = deserializer.read_integer(&INTEGER).unwrap();
453        assert_eq!(value, 0); // Mock deserializer always returns 0
454    }
455}