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, Blob, 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: &Blob) -> Result<(), SerdeError> {
149        self.write_member_key(schema);
150        self.encoder.blob(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
181    use crate::codec::CborCodec;
182
183    fn round_trip(f: impl FnOnce(&mut CborSerializer)) -> Vec<u8> {
184        let codec = CborCodec::default();
185        let mut ser = codec.create_serializer();
186        f(&mut ser);
187        ser.finish()
188    }
189
190    #[test]
191    fn test_write_boolean() {
192        let bytes = round_trip(|s| s.write_boolean(&BOOLEAN, true).unwrap());
193        let mut dec = crate::Decoder::new(&bytes);
194        assert_eq!(dec.boolean().unwrap(), true);
195    }
196
197    #[test]
198    fn test_write_integer() {
199        let bytes = round_trip(|s| s.write_integer(&INTEGER, 42).unwrap());
200        let mut dec = crate::Decoder::new(&bytes);
201        assert_eq!(dec.integer().unwrap(), 42);
202    }
203
204    #[test]
205    fn test_write_long() {
206        let bytes = round_trip(|s| s.write_long(&LONG, i64::MAX).unwrap());
207        let mut dec = crate::Decoder::new(&bytes);
208        assert_eq!(dec.long().unwrap(), i64::MAX);
209    }
210
211    #[test]
212    fn test_write_float_nan() {
213        let bytes = round_trip(|s| s.write_float(&FLOAT, f32::NAN).unwrap());
214        let mut dec = crate::Decoder::new(&bytes);
215        assert!(dec.float().unwrap().is_nan());
216    }
217
218    #[test]
219    fn test_write_double_infinity() {
220        let bytes = round_trip(|s| s.write_double(&DOUBLE, f64::INFINITY).unwrap());
221        let mut dec = crate::Decoder::new(&bytes);
222        assert_eq!(dec.double().unwrap(), f64::INFINITY);
223    }
224
225    #[test]
226    fn test_write_string() {
227        let bytes = round_trip(|s| s.write_string(&STRING, "hello").unwrap());
228        let mut dec = crate::Decoder::new(&bytes);
229        assert_eq!(dec.str().unwrap().as_ref(), "hello");
230    }
231
232    #[test]
233    fn test_write_blob() {
234        let blob = Blob::new(b"binary data");
235        let bytes = round_trip(|s| s.write_blob(&BLOB, &blob).unwrap());
236        let mut dec = crate::Decoder::new(&bytes);
237        assert_eq!(dec.blob().unwrap(), blob);
238    }
239
240    #[test]
241    fn test_write_timestamp() {
242        let ts = DateTime::from_secs_f64(1700000000.5);
243        let bytes = round_trip(|s| s.write_timestamp(&TIMESTAMP, &ts).unwrap());
244        let mut dec = crate::Decoder::new(&bytes);
245        let decoded = dec.timestamp().unwrap();
246        // Timestamp truncates to millisecond precision
247        assert_eq!(decoded.as_secs_f64(), 1700000000.5);
248    }
249
250    #[test]
251    fn test_write_null() {
252        let bytes = round_trip(|s| s.write_null(&STRING).unwrap());
253        let mut dec = crate::Decoder::new(&bytes);
254        dec.null().unwrap();
255    }
256
257    #[test]
258    fn test_write_list() {
259        let list_schema = Schema::new(shape_id!("test", "List"), ShapeType::List);
260        let bytes = round_trip(|s| {
261            s.write_list(&list_schema, &|s| {
262                s.write_integer(&INTEGER, 1)?;
263                s.write_integer(&INTEGER, 2)?;
264                s.write_integer(&INTEGER, 3)?;
265                Ok(())
266            })
267            .unwrap()
268        });
269        let mut dec = crate::Decoder::new(&bytes);
270        // Indefinite-length array
271        let len = dec.list().unwrap();
272        assert!(len.is_none()); // indefinite
273        assert_eq!(dec.integer().unwrap(), 1);
274        assert_eq!(dec.integer().unwrap(), 2);
275        assert_eq!(dec.integer().unwrap(), 3);
276    }
277
278    #[test]
279    fn test_write_struct() {
280        static NAME_MEMBER: Schema =
281            Schema::new_member(shape_id!("test", "Struct"), ShapeType::String, "name", 0);
282        static AGE_MEMBER: Schema =
283            Schema::new_member(shape_id!("test", "Struct"), ShapeType::Integer, "age", 1);
284        static STRUCT_SCHEMA: Schema = Schema::new_struct(
285            shape_id!("test", "Struct"),
286            ShapeType::Structure,
287            &[&NAME_MEMBER, &AGE_MEMBER],
288        );
289
290        struct TestStruct;
291        impl SerializableStruct for TestStruct {
292            fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
293                s.write_string(&NAME_MEMBER, "Alice")?;
294                s.write_integer(&AGE_MEMBER, 30)?;
295                Ok(())
296            }
297        }
298
299        let bytes = round_trip(|s| s.write_struct(&STRUCT_SCHEMA, &TestStruct).unwrap());
300        let mut dec = crate::Decoder::new(&bytes);
301        let len = dec.map().unwrap();
302        assert!(len.is_none()); // indefinite-length map
303        assert_eq!(dec.str().unwrap().as_ref(), "name");
304        assert_eq!(dec.str().unwrap().as_ref(), "Alice");
305        assert_eq!(dec.str().unwrap().as_ref(), "age");
306        assert_eq!(dec.integer().unwrap(), 30);
307    }
308
309    #[test]
310    fn test_write_map() {
311        let map_schema = Schema::new(shape_id!("test", "Map"), ShapeType::Map);
312        let bytes = round_trip(|s| {
313            s.write_map(&map_schema, &|s| {
314                s.write_string(&STRING, "key1")?;
315                s.write_string(&STRING, "val1")?;
316                Ok(())
317            })
318            .unwrap()
319        });
320        let mut dec = crate::Decoder::new(&bytes);
321        let len = dec.map().unwrap();
322        assert!(len.is_none());
323        assert_eq!(dec.str().unwrap().as_ref(), "key1");
324        assert_eq!(dec.str().unwrap().as_ref(), "val1");
325    }
326}