1use std::collections::HashMap;
38
39use aws_smithy_types::{BigDecimal, BigInteger, DateTime, DiscriminatedDocument, Document, Number};
40
41use crate::serde::{SerdeError, SerializableStruct, ShapeSerializer};
42use crate::Schema;
43
44#[derive(Debug, Default)]
90pub struct DocumentShapeSerializer {
91 stack: Vec<Frame>,
93 finished: Option<Document>,
96 root_discriminator: Option<String>,
101}
102
103#[derive(Debug)]
105enum Frame {
106 Struct {
107 members: HashMap<String, Document>,
108 },
109 List(Vec<Document>),
110 Map {
111 entries: HashMap<String, Document>,
112 pending_key: Option<String>,
116 },
117}
118
119impl DocumentShapeSerializer {
120 pub fn new() -> Self {
122 Self::default()
123 }
124
125 pub fn finish(self) -> Result<DiscriminatedDocument, SerdeError> {
138 if !self.stack.is_empty() {
139 return Err(SerdeError::custom(format!(
140 "DocumentShapeSerializer::finish called with {} unfinished container(s) on the stack",
141 self.stack.len()
142 )));
143 }
144 let document = self.finished.ok_or_else(|| {
145 SerdeError::custom(
146 "DocumentShapeSerializer::finish called before any value was written",
147 )
148 })?;
149 let mut wrapper = DiscriminatedDocument::new(document);
150 if let Some(d) = self.root_discriminator {
151 wrapper = wrapper.with_discriminator(d);
152 }
153 Ok(wrapper)
154 }
155
156 pub fn write_struct(
175 &mut self,
176 schema: &Schema<'_>,
177 value: &dyn SerializableStruct,
178 ) -> Result<(), SerdeError> {
179 let is_top_level = self.stack.is_empty() && self.finished.is_none();
180 if is_top_level {
181 self.root_discriminator = Some(schema.shape_id().as_str().to_string());
182 }
183 <Self as ShapeSerializer>::write_struct(self, schema, value)
189 }
190
191 fn commit_value(&mut self, schema: &Schema<'_>, value: Document) -> Result<(), SerdeError> {
194 match self.stack.last_mut() {
195 None => {
196 if self.finished.is_some() {
197 return Err(SerdeError::custom(
198 "DocumentShapeSerializer received multiple top-level writes",
199 ));
200 }
201 self.finished = Some(value);
202 Ok(())
203 }
204 Some(Frame::Struct { members }) => {
205 let name = schema.member_name().ok_or_else(|| {
206 SerdeError::custom(format!(
207 "writing a struct member requires a member schema with member_name set; got schema for {}",
208 schema.shape_id().as_str()
209 ))
210 })?;
211 members.insert(name.to_string(), value);
212 Ok(())
213 }
214 Some(Frame::List(items)) => {
215 items.push(value);
216 Ok(())
217 }
218 Some(Frame::Map {
219 entries,
220 pending_key,
221 }) => {
222 if let Some(key) = pending_key.take() {
223 entries.insert(key, value);
224 } else {
225 let key = match value {
229 Document::String(s) => s,
230 other => {
231 return Err(SerdeError::custom(format!(
232 "map key must be a String document; got {}",
233 shape_kind_name(&other),
234 )))
235 }
236 };
237 *pending_key = Some(key);
238 }
239 Ok(())
240 }
241 }
242 }
243}
244
245fn shape_kind_name(d: &Document) -> &'static str {
248 match d {
249 Document::Null => "null",
250 Document::Bool(_) => "boolean",
251 Document::Number(_) => "number",
252 Document::String(_) => "string",
253 Document::Array(_) => "list",
254 Document::Object(_) => "map",
255 }
256}
257
258impl ShapeSerializer for DocumentShapeSerializer {
259 fn write_struct(
260 &mut self,
261 schema: &Schema<'_>,
262 value: &dyn SerializableStruct,
263 ) -> Result<(), SerdeError> {
264 self.stack.push(Frame::Struct {
269 members: HashMap::new(),
270 });
271 value.serialize_members(self)?;
272 let frame = self.stack.pop().expect("frame just pushed");
273 let members = match frame {
274 Frame::Struct { members } => members,
275 _ => {
276 return Err(SerdeError::custom(
277 "DocumentShapeSerializer struct frame replaced by a different frame kind during serialization",
278 ));
279 }
280 };
281 self.commit_value(schema, Document::Object(members))
282 }
283
284 fn write_list(
285 &mut self,
286 schema: &Schema<'_>,
287 write_elements: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
288 ) -> Result<(), SerdeError> {
289 self.stack.push(Frame::List(Vec::new()));
290 write_elements(self)?;
291 let frame = self.stack.pop().expect("frame just pushed");
292 let items = match frame {
293 Frame::List(items) => items,
294 _ => {
295 return Err(SerdeError::custom(
296 "DocumentShapeSerializer list frame replaced by a different frame kind during serialization",
297 ));
298 }
299 };
300 self.commit_value(schema, Document::Array(items))
301 }
302
303 fn write_map(
304 &mut self,
305 schema: &Schema<'_>,
306 write_entries: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
307 ) -> Result<(), SerdeError> {
308 self.stack.push(Frame::Map {
309 entries: HashMap::new(),
310 pending_key: None,
311 });
312 write_entries(self)?;
313 let frame = self.stack.pop().expect("frame just pushed");
314 let (entries, pending_key) = match frame {
315 Frame::Map {
316 entries,
317 pending_key,
318 } => (entries, pending_key),
319 _ => {
320 return Err(SerdeError::custom(
321 "DocumentShapeSerializer map frame replaced by a different frame kind during serialization",
322 ));
323 }
324 };
325 if pending_key.is_some() {
326 return Err(SerdeError::custom(
327 "map ended with an unmatched key (key written without a matching value)",
328 ));
329 }
330 self.commit_value(schema, Document::Object(entries))
331 }
332
333 fn write_boolean(&mut self, schema: &Schema<'_>, value: bool) -> Result<(), SerdeError> {
334 self.commit_value(schema, Document::Bool(value))
335 }
336
337 fn write_byte(&mut self, schema: &Schema<'_>, value: i8) -> Result<(), SerdeError> {
338 self.commit_value(schema, Document::Number(signed_to_number(value as i64)))
340 }
341
342 fn write_short(&mut self, schema: &Schema<'_>, value: i16) -> Result<(), SerdeError> {
343 self.commit_value(schema, Document::Number(signed_to_number(value as i64)))
344 }
345
346 fn write_integer(&mut self, schema: &Schema<'_>, value: i32) -> Result<(), SerdeError> {
347 self.commit_value(schema, Document::Number(signed_to_number(value as i64)))
348 }
349
350 fn write_long(&mut self, schema: &Schema<'_>, value: i64) -> Result<(), SerdeError> {
351 self.commit_value(schema, Document::Number(signed_to_number(value)))
352 }
353
354 fn write_float(&mut self, schema: &Schema<'_>, value: f32) -> Result<(), SerdeError> {
355 self.commit_value(schema, Document::Number(Number::Float(value as f64)))
356 }
357
358 fn write_double(&mut self, schema: &Schema<'_>, value: f64) -> Result<(), SerdeError> {
359 self.commit_value(schema, Document::Number(Number::Float(value)))
360 }
361
362 fn write_big_integer(
363 &mut self,
364 schema: &Schema<'_>,
365 value: &BigInteger,
366 ) -> Result<(), SerdeError> {
367 self.commit_value(schema, Document::String(value.as_ref().to_string()))
372 }
373
374 fn write_big_decimal(
375 &mut self,
376 schema: &Schema<'_>,
377 value: &BigDecimal,
378 ) -> Result<(), SerdeError> {
379 self.commit_value(schema, Document::String(value.as_ref().to_string()))
382 }
383
384 fn write_string(&mut self, schema: &Schema<'_>, value: &str) -> Result<(), SerdeError> {
385 self.commit_value(schema, Document::String(value.to_string()))
386 }
387
388 fn write_blob(
389 &mut self,
390 schema: &Schema<'_>,
391 value: aws_smithy_types::Blob,
392 ) -> Result<(), SerdeError> {
393 self.commit_value(
398 schema,
399 Document::String(aws_smithy_types::base64::encode(value.as_ref())),
400 )
401 }
402
403 fn write_timestamp(&mut self, schema: &Schema<'_>, value: &DateTime) -> Result<(), SerdeError> {
404 self.commit_value(schema, Document::Number(timestamp_to_number(*value)))
411 }
412
413 fn write_document(&mut self, schema: &Schema<'_>, value: &Document) -> Result<(), SerdeError> {
414 self.commit_value(schema, value.clone())
416 }
417
418 fn write_null(&mut self, schema: &Schema<'_>) -> Result<(), SerdeError> {
419 self.commit_value(schema, Document::Null)
420 }
421}
422
423fn signed_to_number(v: i64) -> Number {
427 if v >= 0 {
428 Number::PosInt(v as u64)
429 } else {
430 Number::NegInt(v)
431 }
432}
433
434fn timestamp_to_number(value: DateTime) -> Number {
442 if value.has_subsec_nanos() {
443 Number::Float(value.as_secs_f64())
444 } else {
445 signed_to_number(value.secs())
446 }
447}
448
449#[cfg(test)]
450mod tests {
451 use super::*;
452 use crate::serde::{SerdeError, SerializableStruct, ShapeSerializer};
453 use crate::{prelude, shape_id, Schema, ShapeId, ShapeType};
454
455 const PERSON_ID: ShapeId<'static> = shape_id!("smithy.example", "Person");
458 const PERSON_NAME_ID: ShapeId<'static> = shape_id!("smithy.example", "Person", "name");
459 const PERSON_AGE_ID: ShapeId<'static> = shape_id!("smithy.example", "Person", "age");
460
461 static PERSON_NAME_MEMBER: Schema<'static> =
462 Schema::new_member(PERSON_NAME_ID, ShapeType::String, "name", 0);
463 static PERSON_AGE_MEMBER: Schema<'static> =
464 Schema::new_member(PERSON_AGE_ID, ShapeType::Integer, "age", 1);
465 static PERSON_SCHEMA: Schema<'static> = Schema::new_struct(
466 PERSON_ID,
467 ShapeType::Structure,
468 &[&PERSON_NAME_MEMBER, &PERSON_AGE_MEMBER],
469 );
470
471 struct Person {
473 name: String,
474 age: i32,
475 }
476
477 impl SerializableStruct for Person {
478 fn schema(&self) -> &Schema<'_> {
479 &PERSON_SCHEMA
480 }
481
482 fn serialize_members(&self, ser: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
483 ser.write_string(&PERSON_NAME_MEMBER, &self.name)?;
484 ser.write_integer(&PERSON_AGE_MEMBER, self.age)?;
485 Ok(())
486 }
487 }
488
489 #[test]
492 fn top_level_scalar_string_becomes_root() {
493 let mut ser = DocumentShapeSerializer::new();
494 ser.write_string(&prelude::STRING, "hello").unwrap();
495 let doc = ser.finish().unwrap();
496 assert_eq!(doc.document().as_string(), Some("hello"));
497 assert!(doc.discriminator().is_none());
498 }
499
500 #[test]
501 fn top_level_scalar_integer_becomes_root() {
502 let mut ser = DocumentShapeSerializer::new();
503 ser.write_integer(&prelude::INTEGER, 42).unwrap();
504 let doc = ser.finish().unwrap();
505 assert!(matches!(
507 doc.document().as_number(),
508 Some(Number::PosInt(42))
509 ));
510 }
511
512 #[test]
513 fn top_level_null_becomes_root() {
514 let mut ser = DocumentShapeSerializer::new();
515 ser.write_null(&prelude::STRING).unwrap();
516 let doc = ser.finish().unwrap();
517 assert!(matches!(doc.document(), Document::Null));
518 }
519
520 #[test]
523 fn write_struct_produces_object_with_discriminator() {
524 let mut ser = DocumentShapeSerializer::new();
525 ser.write_struct(
526 &PERSON_SCHEMA,
527 &Person {
528 name: "Iago".into(),
529 age: 7,
530 },
531 )
532 .unwrap();
533 let doc = ser.finish().unwrap();
534
535 assert_eq!(doc.discriminator(), Some("smithy.example#Person"));
536 let map = doc.document().as_object().unwrap();
537 assert!(matches!(map.get("name"), Some(Document::String(s)) if s == "Iago"));
538 assert!(matches!(map.get("age"), Some(Document::Number(_))));
539 }
540
541 #[test]
544 fn write_list_of_strings() {
545 let mut ser = DocumentShapeSerializer::new();
546 let values = ["a", "b", "c"];
547 ser.write_list(&prelude::DOCUMENT, &|inner| {
548 for v in &values {
549 inner.write_string(&prelude::STRING, v)?;
550 }
551 Ok(())
552 })
553 .unwrap();
554 let doc = ser.finish().unwrap();
555 let items = doc.document().as_array().unwrap();
556 assert_eq!(items.len(), 3);
557 assert!(matches!(&items[0], Document::String(s) if s == "a"));
558 assert!(matches!(&items[2], Document::String(s) if s == "c"));
559 }
560
561 #[test]
562 fn write_sparse_list_with_nulls() {
563 let mut ser = DocumentShapeSerializer::new();
564 ser.write_list(&prelude::DOCUMENT, &|inner| {
565 inner.write_string(&prelude::STRING, "first")?;
566 inner.write_null(&prelude::STRING)?;
567 inner.write_string(&prelude::STRING, "third")?;
568 Ok(())
569 })
570 .unwrap();
571 let doc = ser.finish().unwrap();
572 let items = doc.document().as_array().unwrap();
573 assert_eq!(items.len(), 3);
574 assert!(matches!(&items[0], Document::String(s) if s == "first"));
575 assert!(matches!(&items[1], Document::Null));
576 assert!(matches!(&items[2], Document::String(s) if s == "third"));
577 }
578
579 #[test]
582 fn write_map_of_strings() {
583 let mut ser = DocumentShapeSerializer::new();
584 ser.write_map(&prelude::DOCUMENT, &|inner| {
585 inner.write_string(&prelude::STRING, "key1")?;
586 inner.write_string(&prelude::STRING, "value1")?;
587 inner.write_string(&prelude::STRING, "key2")?;
588 inner.write_string(&prelude::STRING, "value2")?;
589 Ok(())
590 })
591 .unwrap();
592 let doc = ser.finish().unwrap();
593 let map = doc.document().as_object().unwrap();
594 assert_eq!(map.len(), 2);
595 assert!(matches!(map.get("key1"), Some(Document::String(s)) if s == "value1"));
596 assert!(matches!(map.get("key2"), Some(Document::String(s)) if s == "value2"));
597 }
598
599 #[test]
600 fn write_map_with_non_string_key_errors() {
601 let mut ser = DocumentShapeSerializer::new();
602 let result = ser.write_map(&prelude::DOCUMENT, &|inner| {
603 inner.write_integer(&prelude::INTEGER, 1)?;
604 inner.write_string(&prelude::STRING, "value")?;
605 Ok(())
606 });
607 let err = result.expect_err("non-string map key should error");
608 let msg = format!("{err}");
609 assert!(
610 msg.contains("map key must be a String document"),
611 "unexpected error message: {msg}"
612 );
613 }
614
615 #[test]
616 fn write_map_with_unmatched_key_errors() {
617 let mut ser = DocumentShapeSerializer::new();
618 let result = ser.write_map(&prelude::DOCUMENT, &|inner| {
619 inner.write_string(&prelude::STRING, "lonely-key")?;
620 Ok(())
621 });
622 let err = result.expect_err("trailing key without value should error");
623 let msg = format!("{err}");
624 assert!(
625 msg.contains("unmatched key"),
626 "unexpected error message: {msg}"
627 );
628 }
629
630 #[test]
633 fn nested_struct_in_map_round_trips_data_but_loses_inner_discriminator() {
634 let mut ser = DocumentShapeSerializer::new();
640 ser.write_map(&prelude::DOCUMENT, &|inner| {
641 inner.write_string(&prelude::STRING, "owner")?;
642 inner.write_struct(
643 &PERSON_SCHEMA,
644 &Person {
645 name: "Alex".into(),
646 age: 30,
647 },
648 )?;
649 Ok(())
650 })
651 .unwrap();
652 let doc = ser.finish().unwrap();
653 let map = doc.document().as_object().unwrap();
654 let nested = map.get("owner").unwrap();
655 let nested_members = nested.as_object().unwrap();
656 assert!(matches!(
657 nested_members.get("name"),
658 Some(Document::String(s)) if s == "Alex"
659 ));
660 }
661
662 #[test]
665 fn write_document_commits_value_directly() {
666 let nested = Document::Object(HashMap::from([(
667 "foo".to_string(),
668 Document::String("bar".to_string()),
669 )]));
670 let mut ser = DocumentShapeSerializer::new();
671 ser.write_document(&prelude::DOCUMENT, &nested).unwrap();
672 let doc = ser.finish().unwrap();
673 let map = doc.document().as_object().unwrap();
674 assert!(matches!(map.get("foo"), Some(Document::String(s)) if s == "bar"));
675 }
676
677 #[test]
680 fn finish_with_no_writes_errors() {
681 let ser = DocumentShapeSerializer::new();
682 let err = ser.finish().expect_err("empty serializer should error");
683 let msg = format!("{err}");
684 assert!(
685 msg.contains("before any value was written"),
686 "unexpected error message: {msg}"
687 );
688 }
689
690 #[test]
691 fn double_top_level_write_errors() {
692 let mut ser = DocumentShapeSerializer::new();
693 ser.write_string(&prelude::STRING, "first").unwrap();
694 let err = ser
695 .write_string(&prelude::STRING, "second")
696 .expect_err("second top-level write should error");
697 let msg = format!("{err}");
698 assert!(
699 msg.contains("multiple top-level writes"),
700 "unexpected error message: {msg}"
701 );
702 }
703}