Skip to main content

aws_smithy_cbor/codec/
serializer.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! CBOR serializer implementation.
7
8use aws_smithy_schema::codec::FinishSerializer;
9use aws_smithy_schema::serde::{SerdeError, SerializableStruct, ShapeSerializer};
10use aws_smithy_schema::Schema;
11use aws_smithy_types::{BigDecimal, BigInteger, DateTime, Document};
12
13/// CBOR serializer that implements the ShapeSerializer trait.
14///
15/// Wraps the existing optimized `Encoder` which uses `minicbor` with
16/// infallible writes to `Vec<u8>`.
17pub struct CborSerializer {
18    encoder: crate::Encoder,
19}
20
21impl CborSerializer {
22    pub(crate) fn new() -> Self {
23        Self {
24            encoder: crate::Encoder::new(Vec::new()),
25        }
26    }
27
28    /// Writes the member name as a CBOR text string key if this schema is a struct member.
29    #[inline]
30    fn write_member_key(&mut self, schema: &Schema) {
31        if let Some(name) = schema.member_name() {
32            self.encoder.str(name);
33        }
34    }
35}
36
37impl FinishSerializer for CborSerializer {
38    fn finish(self) -> Vec<u8> {
39        self.encoder.into_writer()
40    }
41}
42
43impl ShapeSerializer for CborSerializer {
44    fn write_struct(
45        &mut self,
46        schema: &Schema,
47        value: &dyn SerializableStruct,
48    ) -> Result<(), SerdeError> {
49        self.write_member_key(schema);
50        self.encoder.begin_map();
51        value.serialize_members(self)?;
52        self.encoder.end();
53        Ok(())
54    }
55
56    fn write_list(
57        &mut self,
58        schema: &Schema,
59        write_elements: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
60    ) -> Result<(), SerdeError> {
61        self.write_member_key(schema);
62        self.encoder.begin_array();
63        write_elements(self)?;
64        self.encoder.end();
65        Ok(())
66    }
67
68    fn write_map(
69        &mut self,
70        schema: &Schema,
71        write_entries: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
72    ) -> Result<(), SerdeError> {
73        self.write_member_key(schema);
74        self.encoder.begin_map();
75        write_entries(self)?;
76        self.encoder.end();
77        Ok(())
78    }
79
80    fn write_boolean(&mut self, schema: &Schema, value: bool) -> Result<(), SerdeError> {
81        self.write_member_key(schema);
82        self.encoder.boolean(value);
83        Ok(())
84    }
85
86    fn write_byte(&mut self, schema: &Schema, value: i8) -> Result<(), SerdeError> {
87        self.write_member_key(schema);
88        self.encoder.byte(value);
89        Ok(())
90    }
91
92    fn write_short(&mut self, schema: &Schema, value: i16) -> Result<(), SerdeError> {
93        self.write_member_key(schema);
94        self.encoder.short(value);
95        Ok(())
96    }
97
98    fn write_integer(&mut self, schema: &Schema, value: i32) -> Result<(), SerdeError> {
99        self.write_member_key(schema);
100        self.encoder.integer(value);
101        Ok(())
102    }
103
104    fn write_long(&mut self, schema: &Schema, value: i64) -> Result<(), SerdeError> {
105        self.write_member_key(schema);
106        self.encoder.long(value);
107        Ok(())
108    }
109
110    fn write_float(&mut self, schema: &Schema, value: f32) -> Result<(), SerdeError> {
111        self.write_member_key(schema);
112        self.encoder.float(value);
113        Ok(())
114    }
115
116    fn write_double(&mut self, schema: &Schema, value: f64) -> Result<(), SerdeError> {
117        self.write_member_key(schema);
118        self.encoder.double(value);
119        Ok(())
120    }
121
122    fn write_big_integer(
123        &mut self,
124        _schema: &Schema,
125        _value: &BigInteger,
126    ) -> Result<(), SerdeError> {
127        Err(SerdeError::UnsupportedOperation {
128            message: "CBOR big integer not yet supported (smithy-rs#4611)".into(),
129        })
130    }
131
132    fn write_big_decimal(
133        &mut self,
134        _schema: &Schema,
135        _value: &BigDecimal,
136    ) -> Result<(), SerdeError> {
137        Err(SerdeError::UnsupportedOperation {
138            message: "CBOR big decimal not yet supported (smithy-rs#4611)".into(),
139        })
140    }
141
142    fn write_string(&mut self, schema: &Schema, value: &str) -> Result<(), SerdeError> {
143        self.write_member_key(schema);
144        self.encoder.str(value);
145        Ok(())
146    }
147
148    fn write_blob(&mut self, schema: &Schema, value: &[u8]) -> Result<(), SerdeError> {
149        self.write_member_key(schema);
150        self.encoder.blob_bytes(value);
151        Ok(())
152    }
153
154    fn write_timestamp(&mut self, schema: &Schema, value: &DateTime) -> Result<(), SerdeError> {
155        self.write_member_key(schema);
156        self.encoder.timestamp(value);
157        Ok(())
158    }
159
160    fn write_document(&mut self, _schema: &Schema, _value: &Document) -> Result<(), SerdeError> {
161        Err(SerdeError::UnsupportedOperation {
162            message: "document types are not supported by rpcv2Cbor protocol".into(),
163        })
164    }
165
166    fn write_null(&mut self, schema: &Schema) -> Result<(), SerdeError> {
167        self.write_member_key(schema);
168        self.encoder.null();
169        Ok(())
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176    use aws_smithy_schema::codec::{Codec, FinishSerializer};
177    use aws_smithy_schema::prelude::*;
178    use aws_smithy_schema::serde::ShapeSerializer;
179    use aws_smithy_schema::{shape_id, ShapeType};
180    use aws_smithy_types::Blob;
181
182    use crate::codec::CborCodec;
183
184    fn round_trip(f: impl FnOnce(&mut CborSerializer)) -> Vec<u8> {
185        let codec = CborCodec::default();
186        let mut ser = codec.create_serializer();
187        f(&mut ser);
188        ser.finish()
189    }
190
191    #[test]
192    fn test_write_boolean() {
193        let bytes = round_trip(|s| s.write_boolean(&BOOLEAN, true).unwrap());
194        let mut dec = crate::Decoder::new(&bytes);
195        assert_eq!(dec.boolean().unwrap(), true);
196    }
197
198    #[test]
199    fn test_write_integer() {
200        let bytes = round_trip(|s| s.write_integer(&INTEGER, 42).unwrap());
201        let mut dec = crate::Decoder::new(&bytes);
202        assert_eq!(dec.integer().unwrap(), 42);
203    }
204
205    #[test]
206    fn test_write_long() {
207        let bytes = round_trip(|s| s.write_long(&LONG, i64::MAX).unwrap());
208        let mut dec = crate::Decoder::new(&bytes);
209        assert_eq!(dec.long().unwrap(), i64::MAX);
210    }
211
212    #[test]
213    fn test_write_float_nan() {
214        let bytes = round_trip(|s| s.write_float(&FLOAT, f32::NAN).unwrap());
215        let mut dec = crate::Decoder::new(&bytes);
216        assert!(dec.float().unwrap().is_nan());
217    }
218
219    #[test]
220    fn test_write_double_infinity() {
221        let bytes = round_trip(|s| s.write_double(&DOUBLE, f64::INFINITY).unwrap());
222        let mut dec = crate::Decoder::new(&bytes);
223        assert_eq!(dec.double().unwrap(), f64::INFINITY);
224    }
225
226    #[test]
227    fn test_write_string() {
228        let bytes = round_trip(|s| s.write_string(&STRING, "hello").unwrap());
229        let mut dec = crate::Decoder::new(&bytes);
230        assert_eq!(dec.str().unwrap().as_ref(), "hello");
231    }
232
233    #[test]
234    fn test_write_blob() {
235        let blob = Blob::new(b"binary data");
236        let bytes = round_trip(|s| s.write_blob(&BLOB, blob.as_ref()).unwrap());
237        let mut dec = crate::Decoder::new(&bytes);
238        assert_eq!(dec.blob().unwrap(), blob);
239    }
240
241    #[test]
242    fn test_write_timestamp() {
243        let ts = DateTime::from_secs_f64(1700000000.5);
244        let bytes = round_trip(|s| s.write_timestamp(&TIMESTAMP, &ts).unwrap());
245        let mut dec = crate::Decoder::new(&bytes);
246        let decoded = dec.timestamp().unwrap();
247        // Timestamp truncates to millisecond precision
248        assert_eq!(decoded.as_secs_f64(), 1700000000.5);
249    }
250
251    #[test]
252    fn test_write_null() {
253        let bytes = round_trip(|s| s.write_null(&STRING).unwrap());
254        let mut dec = crate::Decoder::new(&bytes);
255        dec.null().unwrap();
256    }
257
258    #[test]
259    fn test_write_list() {
260        let list_schema = Schema::new(shape_id!("test", "List"), ShapeType::List);
261        let bytes = round_trip(|s| {
262            s.write_list(&list_schema, &|s| {
263                s.write_integer(&INTEGER, 1)?;
264                s.write_integer(&INTEGER, 2)?;
265                s.write_integer(&INTEGER, 3)?;
266                Ok(())
267            })
268            .unwrap()
269        });
270        let mut dec = crate::Decoder::new(&bytes);
271        // Indefinite-length array
272        let len = dec.list().unwrap();
273        assert!(len.is_none()); // indefinite
274        assert_eq!(dec.integer().unwrap(), 1);
275        assert_eq!(dec.integer().unwrap(), 2);
276        assert_eq!(dec.integer().unwrap(), 3);
277    }
278
279    #[test]
280    fn test_write_struct() {
281        static NAME_MEMBER: Schema =
282            Schema::new_member(shape_id!("test", "Struct"), ShapeType::String, "name", 0);
283        static AGE_MEMBER: Schema =
284            Schema::new_member(shape_id!("test", "Struct"), ShapeType::Integer, "age", 1);
285        static STRUCT_SCHEMA: Schema = Schema::new_struct(
286            shape_id!("test", "Struct"),
287            ShapeType::Structure,
288            &[&NAME_MEMBER, &AGE_MEMBER],
289        );
290
291        struct TestStruct;
292        impl SerializableStruct for TestStruct {
293            fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
294                s.write_string(&NAME_MEMBER, "Alice")?;
295                s.write_integer(&AGE_MEMBER, 30)?;
296                Ok(())
297            }
298        }
299
300        let bytes = round_trip(|s| s.write_struct(&STRUCT_SCHEMA, &TestStruct).unwrap());
301        let mut dec = crate::Decoder::new(&bytes);
302        let len = dec.map().unwrap();
303        assert!(len.is_none()); // indefinite-length map
304        assert_eq!(dec.str().unwrap().as_ref(), "name");
305        assert_eq!(dec.str().unwrap().as_ref(), "Alice");
306        assert_eq!(dec.str().unwrap().as_ref(), "age");
307        assert_eq!(dec.integer().unwrap(), 30);
308    }
309
310    #[test]
311    fn test_write_map() {
312        let map_schema = Schema::new(shape_id!("test", "Map"), ShapeType::Map);
313        let bytes = round_trip(|s| {
314            s.write_map(&map_schema, &|s| {
315                s.write_string(&STRING, "key1")?;
316                s.write_string(&STRING, "val1")?;
317                Ok(())
318            })
319            .unwrap()
320        });
321        let mut dec = crate::Decoder::new(&bytes);
322        let len = dec.map().unwrap();
323        assert!(len.is_none());
324        assert_eq!(dec.str().unwrap().as_ref(), "key1");
325        assert_eq!(dec.str().unwrap().as_ref(), "val1");
326    }
327}