aws_smithy_schema/schema/codec/
http_string.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! String codec for HTTP bindings (headers, query params, URI labels).
7
8use crate::serde::{SerdeError, SerializableStruct, ShapeDeserializer, ShapeSerializer};
9use crate::Schema;
10use aws_smithy_types::{BigDecimal, BigInteger, Blob, DateTime, Document};
11
12/// Serializer for converting Smithy types to strings (for HTTP headers, query params, labels).
13pub struct HttpStringSerializer {
14    output: String,
15}
16
17impl HttpStringSerializer {
18    /// Creates a new HTTP string serializer.
19    pub fn new() -> Self {
20        Self {
21            output: String::new(),
22        }
23    }
24
25    /// Finalizes the serialization and returns the output string.
26    pub fn finish(self) -> String {
27        self.output
28    }
29}
30
31impl super::FinishSerializer for HttpStringSerializer {
32    fn finish(self) -> Vec<u8> {
33        self.output.into_bytes()
34    }
35}
36
37impl Default for HttpStringSerializer {
38    fn default() -> Self {
39        Self::new()
40    }
41}
42
43impl ShapeSerializer for HttpStringSerializer {
44    fn write_struct(
45        &mut self,
46        _schema: &Schema,
47        _value: &dyn SerializableStruct,
48    ) -> Result<(), SerdeError> {
49        Err(SerdeError::UnsupportedOperation {
50            message: "structures cannot be serialized to strings".into(),
51        })
52    }
53
54    fn write_list(
55        &mut self,
56        _schema: &Schema,
57        write_elements: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
58    ) -> Result<(), SerdeError> {
59        // Lists are serialized as comma-separated values
60        write_elements(self)
61    }
62
63    fn write_map(
64        &mut self,
65        _schema: &Schema,
66        _write_entries: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
67    ) -> Result<(), SerdeError> {
68        Err(SerdeError::UnsupportedOperation {
69            message: "maps cannot be serialized to strings".into(),
70        })
71    }
72
73    fn write_boolean(&mut self, _schema: &Schema, value: bool) -> Result<(), SerdeError> {
74        if !self.output.is_empty() {
75            self.output.push(',');
76        }
77        self.output.push_str(if value { "true" } else { "false" });
78        Ok(())
79    }
80
81    fn write_byte(&mut self, _schema: &Schema, value: i8) -> Result<(), SerdeError> {
82        if !self.output.is_empty() {
83            self.output.push(',');
84        }
85        self.output.push_str(&value.to_string());
86        Ok(())
87    }
88
89    fn write_short(&mut self, _schema: &Schema, value: i16) -> Result<(), SerdeError> {
90        if !self.output.is_empty() {
91            self.output.push(',');
92        }
93        self.output.push_str(&value.to_string());
94        Ok(())
95    }
96
97    fn write_integer(&mut self, _schema: &Schema, value: i32) -> Result<(), SerdeError> {
98        if !self.output.is_empty() {
99            self.output.push(',');
100        }
101        self.output.push_str(&value.to_string());
102        Ok(())
103    }
104
105    fn write_long(&mut self, _schema: &Schema, value: i64) -> Result<(), SerdeError> {
106        if !self.output.is_empty() {
107            self.output.push(',');
108        }
109        self.output.push_str(&value.to_string());
110        Ok(())
111    }
112
113    fn write_float(&mut self, _schema: &Schema, value: f32) -> Result<(), SerdeError> {
114        if !self.output.is_empty() {
115            self.output.push(',');
116        }
117        if value.is_nan() {
118            self.output.push_str("NaN");
119        } else if value.is_infinite() {
120            self.output.push_str(if value.is_sign_positive() {
121                "Infinity"
122            } else {
123                "-Infinity"
124            });
125        } else {
126            self.output.push_str(&value.to_string());
127        }
128        Ok(())
129    }
130
131    fn write_double(&mut self, _schema: &Schema, value: f64) -> Result<(), SerdeError> {
132        if !self.output.is_empty() {
133            self.output.push(',');
134        }
135        if value.is_nan() {
136            self.output.push_str("NaN");
137        } else if value.is_infinite() {
138            self.output.push_str(if value.is_sign_positive() {
139                "Infinity"
140            } else {
141                "-Infinity"
142            });
143        } else {
144            self.output.push_str(&value.to_string());
145        }
146        Ok(())
147    }
148
149    fn write_big_integer(
150        &mut self,
151        _schema: &Schema,
152        value: &BigInteger,
153    ) -> Result<(), SerdeError> {
154        if !self.output.is_empty() {
155            self.output.push(',');
156        }
157        self.output.push_str(value.as_ref());
158        Ok(())
159    }
160
161    fn write_big_decimal(
162        &mut self,
163        _schema: &Schema,
164        value: &BigDecimal,
165    ) -> Result<(), SerdeError> {
166        if !self.output.is_empty() {
167            self.output.push(',');
168        }
169        self.output.push_str(value.as_ref());
170        Ok(())
171    }
172
173    fn write_string(&mut self, _schema: &Schema, value: &str) -> Result<(), SerdeError> {
174        if !self.output.is_empty() {
175            self.output.push(',');
176        }
177        self.output.push_str(value);
178        Ok(())
179    }
180
181    fn write_blob(&mut self, _schema: &Schema, value: &Blob) -> Result<(), SerdeError> {
182        if !self.output.is_empty() {
183            self.output.push(',');
184        }
185        // Blobs are base64-encoded for string serialization
186        self.output
187            .push_str(&aws_smithy_types::base64::encode(value.as_ref()));
188        Ok(())
189    }
190
191    fn write_timestamp(&mut self, _schema: &Schema, value: &DateTime) -> Result<(), SerdeError> {
192        if !self.output.is_empty() {
193            self.output.push(',');
194        }
195        // Default to HTTP date format for string serialization
196        // TODO(schema): Check schema for timestampFormat trait
197        let formatted = value
198            .fmt(aws_smithy_types::date_time::Format::HttpDate)
199            .map_err(|e| SerdeError::WriteFailed {
200                message: format!("failed to format timestamp: {e}"),
201            })?;
202        self.output.push_str(&formatted);
203        Ok(())
204    }
205
206    fn write_document(&mut self, _schema: &Schema, _value: &Document) -> Result<(), SerdeError> {
207        Err(SerdeError::UnsupportedOperation {
208            message: "documents cannot be serialized to strings".into(),
209        })
210    }
211
212    fn write_null(&mut self, _schema: &Schema) -> Result<(), SerdeError> {
213        Err(SerdeError::UnsupportedOperation {
214            message: "null cannot be serialized to strings".into(),
215        })
216    }
217}
218
219/// Deserializer for parsing Smithy types from strings.
220pub struct HttpStringDeserializer<'a> {
221    input: std::borrow::Cow<'a, str>,
222    position: usize,
223}
224
225impl<'a> HttpStringDeserializer<'a> {
226    /// Creates a new HTTP string deserializer from the given input.
227    pub fn new(input: &'a str) -> Self {
228        Self {
229            input: std::borrow::Cow::Borrowed(input),
230            position: 0,
231        }
232    }
233
234    fn next_value(&mut self) -> Option<&str> {
235        if self.position >= self.input.len() {
236            return None;
237        }
238
239        let start = self.position;
240        if let Some(comma_pos) = self.input[start..].find(',') {
241            let end = start + comma_pos;
242            self.position = end + 1;
243            Some(&self.input[start..end])
244        } else {
245            self.position = self.input.len();
246            Some(&self.input[start..])
247        }
248    }
249
250    fn current_value(&self) -> &str {
251        &self.input[self.position..]
252    }
253}
254
255impl<'a> ShapeDeserializer for HttpStringDeserializer<'a> {
256    fn read_struct(
257        &mut self,
258        _schema: &Schema,
259        _consumer: &mut dyn FnMut(&Schema, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
260    ) -> Result<(), SerdeError> {
261        Err(SerdeError::UnsupportedOperation {
262            message: "structures cannot be deserialized from strings".into(),
263        })
264    }
265
266    fn read_list(
267        &mut self,
268        _schema: &Schema,
269        _consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
270    ) -> Result<(), SerdeError> {
271        // Lists are comma-separated values
272        // The consumer will call read methods for each element
273        Ok(())
274    }
275
276    fn read_map(
277        &mut self,
278        _schema: &Schema,
279        _consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
280    ) -> Result<(), SerdeError> {
281        Err(SerdeError::UnsupportedOperation {
282            message: "maps cannot be deserialized from strings".into(),
283        })
284    }
285
286    fn read_boolean(&mut self, _schema: &Schema) -> Result<bool, SerdeError> {
287        let value = self.next_value().ok_or_else(|| SerdeError::InvalidInput {
288            message: "expected boolean value".into(),
289        })?;
290        value.parse().map_err(|_| SerdeError::InvalidInput {
291            message: format!("invalid boolean: {value}"),
292        })
293    }
294
295    fn read_byte(&mut self, _schema: &Schema) -> Result<i8, SerdeError> {
296        let value = self.next_value().ok_or_else(|| SerdeError::InvalidInput {
297            message: "expected byte value".into(),
298        })?;
299        value.parse().map_err(|_| SerdeError::InvalidInput {
300            message: format!("invalid byte: {value}"),
301        })
302    }
303
304    fn read_short(&mut self, _schema: &Schema) -> Result<i16, SerdeError> {
305        let value = self.next_value().ok_or_else(|| SerdeError::InvalidInput {
306            message: "expected short value".into(),
307        })?;
308        value.parse().map_err(|_| SerdeError::InvalidInput {
309            message: format!("invalid short: {value}"),
310        })
311    }
312
313    fn read_integer(&mut self, _schema: &Schema) -> Result<i32, SerdeError> {
314        let value = self.next_value().ok_or_else(|| SerdeError::InvalidInput {
315            message: "expected integer value".into(),
316        })?;
317        value.parse().map_err(|_| SerdeError::InvalidInput {
318            message: format!("invalid integer: {value}"),
319        })
320    }
321
322    fn read_long(&mut self, _schema: &Schema) -> Result<i64, SerdeError> {
323        let value = self.next_value().ok_or_else(|| SerdeError::InvalidInput {
324            message: "expected long value".into(),
325        })?;
326        value.parse().map_err(|_| SerdeError::InvalidInput {
327            message: format!("invalid long: {value}"),
328        })
329    }
330
331    fn read_float(&mut self, _schema: &Schema) -> Result<f32, SerdeError> {
332        let value = self.next_value().ok_or_else(|| SerdeError::InvalidInput {
333            message: "expected float value".into(),
334        })?;
335        match value {
336            "NaN" => Ok(f32::NAN),
337            "Infinity" => Ok(f32::INFINITY),
338            "-Infinity" => Ok(f32::NEG_INFINITY),
339            _ => value.parse().map_err(|_| SerdeError::InvalidInput {
340                message: format!("invalid float: {value}"),
341            }),
342        }
343    }
344
345    fn read_double(&mut self, _schema: &Schema) -> Result<f64, SerdeError> {
346        let value = self.next_value().ok_or_else(|| SerdeError::InvalidInput {
347            message: "expected double value".into(),
348        })?;
349        match value {
350            "NaN" => Ok(f64::NAN),
351            "Infinity" => Ok(f64::INFINITY),
352            "-Infinity" => Ok(f64::NEG_INFINITY),
353            _ => value.parse().map_err(|_| SerdeError::InvalidInput {
354                message: format!("invalid double: {value}"),
355            }),
356        }
357    }
358
359    fn read_big_integer(&mut self, _schema: &Schema) -> Result<BigInteger, SerdeError> {
360        let value = self.next_value().ok_or_else(|| SerdeError::InvalidInput {
361            message: "expected big integer value".into(),
362        })?;
363        use std::str::FromStr;
364        BigInteger::from_str(value).map_err(|_| SerdeError::InvalidInput {
365            message: format!("invalid big integer: {value}"),
366        })
367    }
368
369    fn read_big_decimal(&mut self, _schema: &Schema) -> Result<BigDecimal, SerdeError> {
370        let value = self.next_value().ok_or_else(|| SerdeError::InvalidInput {
371            message: "expected big decimal value".into(),
372        })?;
373        use std::str::FromStr;
374        BigDecimal::from_str(value).map_err(|_| SerdeError::InvalidInput {
375            message: format!("invalid big decimal: {value}"),
376        })
377    }
378
379    fn read_string(&mut self, _schema: &Schema) -> Result<String, SerdeError> {
380        self.next_value()
381            .ok_or_else(|| SerdeError::InvalidInput {
382                message: "expected string value".into(),
383            })
384            .map(|s| s.to_string())
385    }
386
387    fn read_blob(&mut self, _schema: &Schema) -> Result<Blob, SerdeError> {
388        let value = self.next_value().ok_or_else(|| SerdeError::InvalidInput {
389            message: "expected blob value".into(),
390        })?;
391        let decoded =
392            aws_smithy_types::base64::decode(value).map_err(|e| SerdeError::InvalidInput {
393                message: format!("invalid base64: {e}"),
394            })?;
395        Ok(Blob::new(decoded))
396    }
397
398    fn read_timestamp(&mut self, _schema: &Schema) -> Result<DateTime, SerdeError> {
399        let value = self.next_value().ok_or_else(|| SerdeError::InvalidInput {
400            message: "expected timestamp value".into(),
401        })?;
402        // Try HTTP date format first, then fall back to other formats
403        // TODO(schema): Check schema for timestampFormat trait
404        DateTime::from_str(value, aws_smithy_types::date_time::Format::HttpDate)
405            .or_else(|_| DateTime::from_str(value, aws_smithy_types::date_time::Format::DateTime))
406            .map_err(|e| SerdeError::InvalidInput {
407                message: format!("invalid timestamp: {e}"),
408            })
409    }
410
411    fn read_document(&mut self, _schema: &Schema) -> Result<Document, SerdeError> {
412        Err(SerdeError::UnsupportedOperation {
413            message: "documents cannot be deserialized from strings".into(),
414        })
415    }
416
417    fn is_null(&self) -> bool {
418        self.current_value().is_empty()
419    }
420
421    fn container_size(&self) -> Option<usize> {
422        // Count commas + 1 for list size estimation
423        Some(self.input.matches(',').count() + 1)
424    }
425}
426
427/// HTTP string codec for serializing/deserializing to/from strings.
428pub struct HttpStringCodec;
429
430impl crate::codec::Codec for HttpStringCodec {
431    type Serializer = HttpStringSerializer;
432    type Deserializer<'a> = HttpStringDeserializer<'a>;
433
434    fn create_serializer(&self) -> Self::Serializer {
435        HttpStringSerializer::new()
436    }
437
438    fn create_deserializer<'a>(&self, input: &'a [u8]) -> Self::Deserializer<'a> {
439        let input_str = std::str::from_utf8(input).unwrap_or("");
440        HttpStringDeserializer::new(input_str)
441    }
442}
443
444#[cfg(test)]
445mod tests {
446    use super::*;
447    use crate::prelude::*;
448
449    #[test]
450    fn test_serialize_boolean() {
451        let mut ser = HttpStringSerializer::new();
452        ser.write_boolean(&BOOLEAN, true).unwrap();
453        assert_eq!(ser.finish(), "true");
454
455        let mut ser = HttpStringSerializer::new();
456        ser.write_boolean(&BOOLEAN, false).unwrap();
457        assert_eq!(ser.finish(), "false");
458    }
459
460    #[test]
461    fn test_serialize_integers() {
462        let mut ser = HttpStringSerializer::new();
463        ser.write_byte(&BYTE, 42).unwrap();
464        assert_eq!(ser.finish(), "42");
465
466        let mut ser = HttpStringSerializer::new();
467        ser.write_integer(&INTEGER, -123).unwrap();
468        assert_eq!(ser.finish(), "-123");
469
470        let mut ser = HttpStringSerializer::new();
471        ser.write_long(&LONG, 9876543210).unwrap();
472        assert_eq!(ser.finish(), "9876543210");
473    }
474
475    #[test]
476    fn test_serialize_floats() {
477        let mut ser = HttpStringSerializer::new();
478        ser.write_float(&FLOAT, 3.15).unwrap();
479        assert_eq!(ser.finish(), "3.15");
480
481        let mut ser = HttpStringSerializer::new();
482        ser.write_float(&FLOAT, f32::NAN).unwrap();
483        assert_eq!(ser.finish(), "NaN");
484
485        let mut ser = HttpStringSerializer::new();
486        ser.write_float(&FLOAT, f32::INFINITY).unwrap();
487        assert_eq!(ser.finish(), "Infinity");
488    }
489
490    #[test]
491    fn test_serialize_string() {
492        let mut ser = HttpStringSerializer::new();
493        ser.write_string(&STRING, "hello world").unwrap();
494        assert_eq!(ser.finish(), "hello world");
495    }
496
497    #[test]
498    fn test_serialize_list() {
499        let mut ser = HttpStringSerializer::new();
500        ser.write_list(&STRING, &|s: &mut dyn ShapeSerializer| {
501            s.write_string(&STRING, "a")?;
502            s.write_string(&STRING, "b")?;
503            s.write_string(&STRING, "c")?;
504            Ok(())
505        })
506        .unwrap();
507        assert_eq!(ser.finish(), "a,b,c");
508    }
509
510    #[test]
511    fn test_serialize_blob() {
512        let mut ser = HttpStringSerializer::new();
513        let blob = Blob::new(vec![1, 2, 3, 4]);
514        ser.write_blob(&BLOB, &blob).unwrap();
515        // Base64 encoding of [1, 2, 3, 4]
516        assert_eq!(ser.finish(), "AQIDBA==");
517    }
518
519    #[test]
520    fn test_deserialize_boolean() {
521        let mut deser = HttpStringDeserializer::new("true");
522        assert!(deser.read_boolean(&BOOLEAN).unwrap());
523
524        let mut deser = HttpStringDeserializer::new("false");
525        assert!(!(deser.read_boolean(&BOOLEAN).unwrap()));
526    }
527
528    #[test]
529    fn test_deserialize_integers() {
530        let mut deser = HttpStringDeserializer::new("42");
531        assert_eq!(deser.read_byte(&BYTE).unwrap(), 42);
532
533        let mut deser = HttpStringDeserializer::new("-123");
534        assert_eq!(deser.read_integer(&INTEGER).unwrap(), -123);
535
536        let mut deser = HttpStringDeserializer::new("9876543210");
537        assert_eq!(deser.read_long(&LONG).unwrap(), 9876543210);
538    }
539
540    #[test]
541    fn test_deserialize_floats() {
542        let mut deser = HttpStringDeserializer::new("3.15");
543        assert!((deser.read_float(&FLOAT).unwrap() - 3.15).abs() < 0.01);
544
545        let mut deser = HttpStringDeserializer::new("NaN");
546        assert!(deser.read_float(&FLOAT).unwrap().is_nan());
547
548        let mut deser = HttpStringDeserializer::new("Infinity");
549        assert_eq!(deser.read_float(&FLOAT).unwrap(), f32::INFINITY);
550    }
551
552    #[test]
553    fn test_deserialize_string() {
554        let mut deser = HttpStringDeserializer::new("hello world");
555        assert_eq!(deser.read_string(&STRING).unwrap(), "hello world");
556    }
557
558    #[test]
559    fn test_deserialize_list() {
560        let mut deser = HttpStringDeserializer::new("a,b,c");
561        let values = vec![
562            deser.read_string(&STRING).unwrap(),
563            deser.read_string(&STRING).unwrap(),
564            deser.read_string(&STRING).unwrap(),
565        ];
566        assert_eq!(values, vec!["a", "b", "c"]);
567    }
568
569    #[test]
570    fn test_deserialize_blob() {
571        let mut deser = HttpStringDeserializer::new("AQIDBA==");
572        let blob = deser.read_blob(&BLOB).unwrap();
573        assert_eq!(blob.as_ref(), &[1, 2, 3, 4]);
574    }
575
576    #[test]
577    fn test_container_size() {
578        let deser = HttpStringDeserializer::new("a,b,c");
579        assert_eq!(deser.container_size(), Some(3));
580
581        let deser = HttpStringDeserializer::new("single");
582        assert_eq!(deser.container_size(), Some(1));
583    }
584
585    #[test]
586    fn test_is_null() {
587        let deser = HttpStringDeserializer::new("");
588        assert!(deser.is_null());
589
590        let deser = HttpStringDeserializer::new("value");
591        assert!(!deser.is_null());
592    }
593
594    #[test]
595    fn test_codec_trait() {
596        use crate::codec::Codec;
597
598        let codec = HttpStringCodec;
599
600        // Test serialization through codec
601        let mut ser = codec.create_serializer();
602        ser.write_string(&STRING, "test").unwrap();
603        let output = ser.finish();
604        assert_eq!(output, "test");
605
606        // Test deserialization through codec
607        let input = b"hello";
608        let mut deser = codec.create_deserializer(input);
609        let result = deser.read_string(&STRING).unwrap();
610        assert_eq!(result, "hello");
611    }
612}