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}