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(
270 &mut self,
271 _schema: &Schema,
272 _value: &aws_smithy_types::Blob,
273 ) -> Result<(), SerdeError> {
274 Ok(())
275 }
276
277 fn write_timestamp(
278 &mut self,
279 _schema: &Schema,
280 _value: &aws_smithy_types::DateTime,
281 ) -> Result<(), SerdeError> {
282 Ok(())
283 }
284
285 fn write_document(
286 &mut self,
287 _schema: &Schema,
288 _value: &aws_smithy_types::Document,
289 ) -> Result<(), SerdeError> {
290 Ok(())
291 }
292
293 fn write_null(&mut self, _schema: &Schema) -> Result<(), SerdeError> {
294 Ok(())
295 }
296 }
297
298 // Mock deserializer
299 struct MockDeserializer<'a> {
300 #[allow(dead_code)]
301 input: &'a [u8],
302 }
303
304 impl<'a> ShapeDeserializer for MockDeserializer<'a> {
305 fn read_struct(
306 &mut self,
307 _schema: &Schema,
308 _consumer: &mut dyn FnMut(
309 &Schema,
310 &mut dyn ShapeDeserializer,
311 ) -> Result<(), SerdeError>,
312 ) -> Result<(), SerdeError> {
313 Ok(())
314 }
315
316 fn read_list(
317 &mut self,
318 _schema: &Schema,
319 _consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
320 ) -> Result<(), SerdeError> {
321 Ok(())
322 }
323
324 fn read_map(
325 &mut self,
326 _schema: &Schema,
327 _consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
328 ) -> Result<(), SerdeError> {
329 Ok(())
330 }
331
332 fn read_boolean(&mut self, _schema: &Schema) -> Result<bool, SerdeError> {
333 Ok(false)
334 }
335
336 fn read_byte(&mut self, _schema: &Schema) -> Result<i8, SerdeError> {
337 Ok(0)
338 }
339
340 fn read_short(&mut self, _schema: &Schema) -> Result<i16, SerdeError> {
341 Ok(0)
342 }
343
344 fn read_integer(&mut self, _schema: &Schema) -> Result<i32, SerdeError> {
345 Ok(0)
346 }
347
348 fn read_long(&mut self, _schema: &Schema) -> Result<i64, SerdeError> {
349 Ok(0)
350 }
351
352 fn read_float(&mut self, _schema: &Schema) -> Result<f32, SerdeError> {
353 Ok(0.0)
354 }
355
356 fn read_double(&mut self, _schema: &Schema) -> Result<f64, SerdeError> {
357 Ok(0.0)
358 }
359
360 fn read_big_integer(
361 &mut self,
362 _schema: &Schema,
363 ) -> Result<aws_smithy_types::BigInteger, SerdeError> {
364 use std::str::FromStr;
365 Ok(aws_smithy_types::BigInteger::from_str("0").unwrap())
366 }
367
368 fn read_big_decimal(
369 &mut self,
370 _schema: &Schema,
371 ) -> Result<aws_smithy_types::BigDecimal, SerdeError> {
372 use std::str::FromStr;
373 Ok(aws_smithy_types::BigDecimal::from_str("0").unwrap())
374 }
375
376 fn read_string(&mut self, _schema: &Schema) -> Result<String, SerdeError> {
377 Ok(String::new())
378 }
379
380 fn read_blob(&mut self, _schema: &Schema) -> Result<aws_smithy_types::Blob, SerdeError> {
381 Ok(aws_smithy_types::Blob::new(Vec::new()))
382 }
383
384 fn read_timestamp(
385 &mut self,
386 _schema: &Schema,
387 ) -> Result<aws_smithy_types::DateTime, SerdeError> {
388 Ok(aws_smithy_types::DateTime::from_secs(0))
389 }
390
391 fn read_document(
392 &mut self,
393 _schema: &Schema,
394 ) -> Result<aws_smithy_types::Document, SerdeError> {
395 Ok(aws_smithy_types::Document::Null)
396 }
397
398 fn is_null(&self) -> bool {
399 false
400 }
401
402 fn container_size(&self) -> Option<usize> {
403 None
404 }
405 }
406
407 // Mock codec
408 struct MockCodec;
409
410 impl Codec for MockCodec {
411 type Serializer = MockSerializer;
412 type Deserializer<'a> = MockDeserializer<'a>;
413
414 fn create_serializer(&self) -> Self::Serializer {
415 MockSerializer { output: Vec::new() }
416 }
417
418 fn create_deserializer<'a>(&self, input: &'a [u8]) -> Self::Deserializer<'a> {
419 MockDeserializer { input }
420 }
421 }
422
423 #[test]
424 fn test_codec_create_serializer() {
425 let codec = MockCodec;
426 let mut serializer = codec.create_serializer();
427
428 // Test that we can use the serializer
429 serializer.write_string(&STRING, "test").unwrap();
430 let output = serializer.finish();
431 assert_eq!(output, Vec::<u8>::new());
432 }
433
434 #[test]
435 fn test_codec_create_deserializer() {
436 let codec = MockCodec;
437 let input = b"test data";
438 let mut deserializer = codec.create_deserializer(input);
439
440 // Test that we can use the deserializer
441 let result = deserializer.read_string(&STRING).unwrap();
442 assert_eq!(result, "");
443 }
444
445 #[test]
446 fn test_codec_roundtrip() {
447 let codec = MockCodec;
448
449 // Serialize
450 let mut serializer = codec.create_serializer();
451 serializer.write_integer(&INTEGER, 42).unwrap();
452 let bytes = serializer.finish();
453
454 // Deserialize
455 let mut deserializer = codec.create_deserializer(&bytes);
456 let value = deserializer.read_integer(&INTEGER).unwrap();
457 assert_eq!(value, 0); // Mock deserializer always returns 0
458 }
459}