1#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))]
7mod de;
8mod discriminated;
9#[cfg(any(
10 all(aws_sdk_unstable, feature = "serde-deserialize"),
11 all(aws_sdk_unstable, feature = "serde-serialize")
12))]
13mod doc_error;
14mod error;
15#[cfg(all(aws_sdk_unstable, feature = "serde-serialize"))]
16mod ser;
17mod settings;
18
19#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))]
20pub use de::from_document;
21pub use discriminated::DiscriminatedDocument;
22#[cfg(any(
23 all(aws_sdk_unstable, feature = "serde-deserialize"),
24 all(aws_sdk_unstable, feature = "serde-serialize")
25))]
26pub use doc_error::DocError;
27pub use error::DocumentError;
28#[cfg(all(aws_sdk_unstable, feature = "serde-serialize"))]
29pub use ser::to_document;
30pub use settings::DocumentSettings;
31
32use crate::Number;
33use std::borrow::Cow;
34use std::collections::HashMap;
35
36#[cfg(any(
37 all(aws_sdk_unstable, feature = "serde-deserialize"),
38 all(aws_sdk_unstable, feature = "serde-serialize")
39))]
40use serde;
41
42#[derive(Clone, Debug, PartialEq)]
49#[cfg_attr(
50 all(aws_sdk_unstable, feature = "serde-serialize"),
51 derive(serde::Serialize)
52)]
53#[cfg_attr(
54 all(aws_sdk_unstable, feature = "serde-deserialize"),
55 derive(serde::Deserialize)
56)]
57#[cfg_attr(
58 any(
59 all(aws_sdk_unstable, feature = "serde-deserialize"),
60 all(aws_sdk_unstable, feature = "serde-serialize")
61 ),
62 serde(untagged)
63)]
64pub enum Document {
65 Object(HashMap<String, Document>),
67 Array(Vec<Document>),
69 Number(Number),
71 String(String),
73 Bool(bool),
75 Null,
77}
78
79impl Document {
80 pub fn as_object(&self) -> Option<&HashMap<String, Document>> {
82 if let Self::Object(object) = self {
83 Some(object)
84 } else {
85 None
86 }
87 }
88
89 pub fn as_object_mut(&mut self) -> Option<&mut HashMap<String, Document>> {
91 if let Self::Object(object) = self {
92 Some(object)
93 } else {
94 None
95 }
96 }
97
98 pub fn as_array(&self) -> Option<&Vec<Document>> {
100 if let Self::Array(array) = self {
101 Some(array)
102 } else {
103 None
104 }
105 }
106
107 pub fn as_array_mut(&mut self) -> Option<&mut Vec<Document>> {
109 if let Self::Array(array) = self {
110 Some(array)
111 } else {
112 None
113 }
114 }
115
116 pub fn as_number(&self) -> Option<&Number> {
118 if let Self::Number(number) = self {
119 Some(number)
120 } else {
121 None
122 }
123 }
124
125 pub fn as_string(&self) -> Option<&str> {
127 if let Self::String(string) = self {
128 Some(string)
129 } else {
130 None
131 }
132 }
133
134 pub fn as_bool(&self) -> Option<bool> {
136 if let Self::Bool(boolean) = self {
137 Some(*boolean)
138 } else {
139 None
140 }
141 }
142
143 pub fn as_null(&self) -> Option<()> {
145 if let Self::Null = self {
146 Some(())
147 } else {
148 None
149 }
150 }
151
152 pub fn is_object(&self) -> bool {
154 matches!(self, Self::Object(_))
155 }
156
157 pub fn is_array(&self) -> bool {
159 matches!(self, Self::Array(_))
160 }
161
162 pub fn is_number(&self) -> bool {
164 matches!(self, Self::Number(_))
165 }
166
167 pub fn is_string(&self) -> bool {
169 matches!(self, Self::String(_))
170 }
171
172 pub fn is_bool(&self) -> bool {
174 matches!(self, Self::Bool(_))
175 }
176
177 pub fn is_null(&self) -> bool {
179 matches!(self, Self::Null)
180 }
181}
182
183impl Default for Document {
185 fn default() -> Self {
186 Self::Null
187 }
188}
189
190impl From<bool> for Document {
191 fn from(value: bool) -> Self {
192 Document::Bool(value)
193 }
194}
195
196impl<'a> From<&'a str> for Document {
197 fn from(value: &'a str) -> Self {
198 Document::String(value.to_string())
199 }
200}
201
202impl<'a> From<Cow<'a, str>> for Document {
203 fn from(value: Cow<'a, str>) -> Self {
204 Document::String(value.into_owned())
205 }
206}
207
208impl From<String> for Document {
209 fn from(value: String) -> Self {
210 Document::String(value)
211 }
212}
213
214impl From<Vec<Document>> for Document {
215 fn from(values: Vec<Document>) -> Self {
216 Document::Array(values)
217 }
218}
219
220impl From<HashMap<String, Document>> for Document {
221 fn from(values: HashMap<String, Document>) -> Self {
222 Document::Object(values)
223 }
224}
225
226impl From<u64> for Document {
227 fn from(value: u64) -> Self {
228 Document::Number(Number::PosInt(value))
229 }
230}
231
232impl From<i64> for Document {
233 fn from(value: i64) -> Self {
234 Document::Number(Number::NegInt(value))
235 }
236}
237
238impl From<i32> for Document {
239 fn from(value: i32) -> Self {
240 Document::Number(Number::NegInt(value as i64))
241 }
242}
243
244impl From<f64> for Document {
245 fn from(value: f64) -> Self {
246 Document::Number(Number::Float(value))
247 }
248}
249
250impl From<Number> for Document {
251 fn from(value: Number) -> Self {
252 Document::Number(value)
253 }
254}
255
256impl<T> From<Option<T>> for Document
257where
258 Document: From<T>,
259{
260 fn from(value: Option<T>) -> Self {
261 match value {
262 Some(inner) => inner.into(),
263 None => Document::Null,
264 }
265 }
266}
267
268pub(crate) fn document_variant_name(d: &Document) -> &'static str {
279 match d {
280 Document::Null => "null",
281 Document::Bool(_) => "boolean",
282 Document::Number(_) => "number",
283 Document::String(_) => "string",
284 Document::Array(_) => "array",
285 Document::Object(_) => "object",
286 }
287}
288
289#[cfg(test)]
290#[cfg(all(
291 aws_sdk_unstable,
292 feature = "serde-serialize",
293 feature = "serde-deserialize"
294))]
295mod test {
296 use super::{from_document, to_document, Document};
297 use crate::Number;
298 use serde::{Deserialize, Serialize};
299 use std::collections::HashMap;
300
301 fn test_to_document_ok<T>(cases: &[(T, Document)])
303 where
304 T: Serialize + std::fmt::Debug,
305 {
306 for (value, expected) in cases {
307 let doc = to_document(value).unwrap();
308 assert_eq!(&doc, expected, "to_document({:?})", value);
309 }
310 }
311
312 fn test_roundtrip<T>(cases: &[T])
314 where
315 T: Serialize + for<'de> Deserialize<'de> + PartialEq + std::fmt::Debug + Clone,
316 {
317 for value in cases {
318 let doc = to_document(value).unwrap();
319 let roundtripped: T = from_document(doc).unwrap();
320 assert_eq!(&roundtripped, value, "roundtrip failed for {:?}", value);
321 }
322 }
323
324 #[test]
329 fn test_null() {
330 test_to_document_ok(&[((), Document::Null)]);
331
332 let v: () = from_document(Document::Null).unwrap();
333 assert_eq!(v, ());
334 }
335
336 #[test]
341 fn test_bool() {
342 test_to_document_ok(&[(true, Document::Bool(true)), (false, Document::Bool(false))]);
343 test_roundtrip(&[true, false]);
344 }
345
346 #[test]
351 fn test_u8() {
352 test_to_document_ok(&[
353 (0u8, Document::Number(Number::PosInt(0))),
354 (u8::MAX, Document::Number(Number::PosInt(u8::MAX as u64))),
355 ]);
356 test_roundtrip(&[0u8, 1, 127, u8::MAX]);
357 }
358
359 #[test]
360 fn test_u16() {
361 test_to_document_ok(&[
362 (0u16, Document::Number(Number::PosInt(0))),
363 (u16::MAX, Document::Number(Number::PosInt(u16::MAX as u64))),
364 ]);
365 test_roundtrip(&[0u16, 1, u16::MAX]);
366 }
367
368 #[test]
369 fn test_u32() {
370 test_to_document_ok(&[
371 (0u32, Document::Number(Number::PosInt(0))),
372 (u32::MAX, Document::Number(Number::PosInt(u32::MAX as u64))),
373 ]);
374 test_roundtrip(&[0u32, 1, u32::MAX]);
375 }
376
377 #[test]
378 fn test_u64() {
379 test_to_document_ok(&[
380 (0u64, Document::Number(Number::PosInt(0))),
381 (u64::MAX, Document::Number(Number::PosInt(u64::MAX))),
382 ]);
383 test_roundtrip(&[0u64, 1, u64::MAX]);
384 }
385
386 #[test]
391 fn test_i8() {
392 test_to_document_ok(&[
393 (0i8, Document::Number(Number::PosInt(0))),
394 (-1i8, Document::Number(Number::NegInt(-1))),
395 (i8::MIN, Document::Number(Number::NegInt(i8::MIN as i64))),
396 (i8::MAX, Document::Number(Number::PosInt(i8::MAX as u64))),
397 ]);
398 test_roundtrip(&[0i8, -1, 1, i8::MIN, i8::MAX]);
399 }
400
401 #[test]
402 fn test_i16() {
403 test_to_document_ok(&[
404 (0i16, Document::Number(Number::PosInt(0))),
405 (i16::MIN, Document::Number(Number::NegInt(i16::MIN as i64))),
406 (i16::MAX, Document::Number(Number::PosInt(i16::MAX as u64))),
407 ]);
408 test_roundtrip(&[0i16, -1, i16::MIN, i16::MAX]);
409 }
410
411 #[test]
412 fn test_i32() {
413 test_to_document_ok(&[
414 (0i32, Document::Number(Number::PosInt(0))),
415 (i32::MIN, Document::Number(Number::NegInt(i32::MIN as i64))),
416 (i32::MAX, Document::Number(Number::PosInt(i32::MAX as u64))),
417 ]);
418 test_roundtrip(&[0i32, -1, i32::MIN, i32::MAX]);
419 }
420
421 #[test]
422 fn test_i64() {
423 test_to_document_ok(&[
424 (0i64, Document::Number(Number::PosInt(0))),
425 (-1i64, Document::Number(Number::NegInt(-1))),
426 (i64::MIN, Document::Number(Number::NegInt(i64::MIN))),
427 (i64::MAX, Document::Number(Number::PosInt(i64::MAX as u64))),
428 ]);
429 test_roundtrip(&[0i64, -1, i64::MIN, i64::MAX]);
430 }
431
432 #[test]
437 fn test_f32() {
438 test_to_document_ok(&[
439 (0.0f32, Document::Number(Number::Float(0.0))),
440 (3.5f32, Document::Number(Number::Float(3.5))),
441 (-1.5f32, Document::Number(Number::Float(-1.5))),
442 ]);
443 test_roundtrip(&[0.0f32, 3.5, -1.5, f32::MIN, f32::MAX]);
444 }
445
446 #[test]
447 fn test_f64() {
448 test_to_document_ok(&[
449 (0.0f64, Document::Number(Number::Float(0.0))),
450 (3.1f64, Document::Number(Number::Float(3.1))),
451 (-1.5f64, Document::Number(Number::Float(-1.5))),
452 (f64::MIN, Document::Number(Number::Float(f64::MIN))),
453 (f64::MAX, Document::Number(Number::Float(f64::MAX))),
454 (f64::EPSILON, Document::Number(Number::Float(f64::EPSILON))),
455 ]);
456 test_roundtrip(&[0.0f64, 3.1, -1.5, 0.5, f64::MIN, f64::MAX]);
457 }
458
459 #[test]
460 fn test_nonfinite_floats() {
461 let doc = to_document(&f64::NAN).unwrap();
463 match doc {
464 Document::Number(Number::Float(v)) => assert!(v.is_nan()),
465 other => panic!("expected NaN float, got {:?}", other),
466 }
467
468 let doc = to_document(&f64::INFINITY).unwrap();
469 assert_eq!(doc, Document::Number(Number::Float(f64::INFINITY)));
470
471 let doc = to_document(&f64::NEG_INFINITY).unwrap();
472 assert_eq!(doc, Document::Number(Number::Float(f64::NEG_INFINITY)));
473 }
474
475 #[test]
480 fn test_string() {
481 test_to_document_ok(&[
482 (String::new(), Document::String(String::new())),
483 ("hello".to_owned(), Document::String("hello".to_owned())),
484 (
485 "with\nnewline".to_owned(),
486 Document::String("with\nnewline".to_owned()),
487 ),
488 (
489 "unicode: \u{1F600}".to_owned(),
490 Document::String("unicode: \u{1F600}".to_owned()),
491 ),
492 ]);
493 test_roundtrip(&[
494 String::new(),
495 "foo".to_owned(),
496 "bar\tbaz".to_owned(),
497 "\u{3A3}".to_owned(),
498 ]);
499 }
500
501 #[test]
502 fn test_str_ref() {
503 let doc = to_document(&"borrowed str").unwrap();
504 assert_eq!(doc, Document::String("borrowed str".to_owned()));
505 }
506
507 #[test]
508 fn test_char() {
509 let doc = to_document(&'a').unwrap();
510 assert_eq!(doc, Document::String("a".to_owned()));
511
512 let doc = to_document(&'\u{1F600}').unwrap();
513 assert_eq!(doc, Document::String("\u{1F600}".to_owned()));
514 }
515
516 #[test]
521 fn test_option() {
522 test_to_document_ok(&[
523 (None::<String>, Document::Null),
524 (
525 Some("jodhpurs".to_owned()),
526 Document::String("jodhpurs".to_owned()),
527 ),
528 ]);
529 test_to_document_ok(&[
530 (None::<u32>, Document::Null),
531 (Some(42u32), Document::Number(Number::PosInt(42))),
532 ]);
533 test_roundtrip(&[None::<u32>, Some(5), Some(0)]);
534 test_roundtrip(&[None::<String>, Some("x".to_owned())]);
535 }
536
537 #[test]
542 fn test_vec_empty() {
543 let doc = to_document(&Vec::<i32>::new()).unwrap();
544 assert_eq!(doc, Document::Array(vec![]));
545
546 let v: Vec<i32> = from_document(Document::Array(vec![])).unwrap();
547 assert_eq!(v, Vec::<i32>::new());
548 }
549
550 #[test]
551 fn test_vec_integers() {
552 test_to_document_ok(&[(
553 vec![1u64, 2, 3],
554 Document::Array(vec![
555 Document::Number(Number::PosInt(1)),
556 Document::Number(Number::PosInt(2)),
557 Document::Number(Number::PosInt(3)),
558 ]),
559 )]);
560 test_roundtrip(&[vec![1i32, -2, 3], vec![], vec![0]]);
561 }
562
563 #[test]
564 fn test_vec_mixed_via_document() {
565 let mixed = vec![
567 Document::Bool(true),
568 Document::Null,
569 Document::String("foo".to_owned()),
570 Document::Number(Number::PosInt(42)),
571 ];
572 let doc = Document::Array(mixed.clone());
573 let roundtripped: Vec<Document> = from_document(doc.clone()).unwrap();
574 assert_eq!(roundtripped, mixed);
575 }
576
577 #[test]
578 fn test_nested_vec() {
579 test_roundtrip(&[
580 vec![vec![1u32, 2], vec![], vec![3]],
581 vec![vec![], vec![], vec![]],
582 ]);
583 }
584
585 #[test]
586 fn test_tuple() {
587 let doc = to_document(&(5u32,)).unwrap();
588 assert_eq!(
589 doc,
590 Document::Array(vec![Document::Number(Number::PosInt(5))])
591 );
592
593 let doc = to_document(&(1u32, "abc", true)).unwrap();
594 assert_eq!(
595 doc,
596 Document::Array(vec![
597 Document::Number(Number::PosInt(1)),
598 Document::String("abc".to_owned()),
599 Document::Bool(true),
600 ])
601 );
602
603 test_roundtrip(&[(1u32, 2u32), (0, u32::MAX)]);
604 test_roundtrip(&[(1i32, "hello".to_owned(), true)]);
605 }
606
607 #[test]
612 fn test_map_empty() {
613 let map: HashMap<String, u32> = HashMap::new();
614 let doc = to_document(&map).unwrap();
615 assert_eq!(doc, Document::Object(HashMap::new()));
616 test_roundtrip(&[HashMap::<String, u32>::new()]);
617 }
618
619 #[test]
620 fn test_map_string_keys() {
621 let mut map = HashMap::new();
622 map.insert("a".to_owned(), 1u32);
623 map.insert("b".to_owned(), 2u32);
624 test_roundtrip(&[map]);
625 }
626
627 #[test]
628 fn test_map_integer_keys() {
629 let mut map = HashMap::new();
631 map.insert(1u32, "one".to_owned());
632 map.insert(2u32, "two".to_owned());
633
634 let doc = to_document(&map).unwrap();
635 assert!(doc.is_object());
636 let obj = doc.as_object().unwrap();
637 assert!(obj.contains_key("1") || obj.contains_key("2"));
638 }
639
640 #[test]
641 fn test_nested_map() {
642 let mut inner = HashMap::new();
643 inner.insert("x".to_owned(), 10u32);
644
645 let mut outer = HashMap::new();
646 outer.insert("inner".to_owned(), inner.clone());
647
648 test_roundtrip(&[outer]);
649 }
650
651 #[test]
656 fn test_struct() {
657 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
658 struct Inner {
659 a: (),
660 b: usize,
661 c: Vec<String>,
662 }
663
664 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
665 struct Outer {
666 inner: Vec<Inner>,
667 }
668
669 let outer = Outer {
670 inner: vec![Inner {
671 a: (),
672 b: 2,
673 c: vec!["abc".to_owned(), "xyz".to_owned()],
674 }],
675 };
676
677 let doc = to_document(&outer).unwrap();
678 assert!(doc.is_object());
679 let roundtripped: Outer = from_document(doc).unwrap();
680 assert_eq!(outer, roundtripped);
681
682 test_roundtrip(&[Outer { inner: vec![] }]);
684 }
685
686 #[test]
687 fn test_newtype_struct() {
688 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
689 struct Wrapper(u32);
690
691 test_to_document_ok(&[(Wrapper(123), Document::Number(Number::PosInt(123)))]);
692 test_roundtrip(&[Wrapper(0), Wrapper(u32::MAX)]);
693 }
694
695 #[test]
696 fn test_unit_struct() {
697 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
698 struct Unit;
699
700 test_to_document_ok(&[(Unit, Document::Null)]);
701 let v: Unit = from_document(Document::Null).unwrap();
702 assert_eq!(v, Unit);
703 }
704
705 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
710 enum Animal {
711 Dog,
712 Frog(String, Vec<isize>),
713 Cat { age: usize, name: String },
714 AntHive(Vec<String>),
715 }
716
717 #[test]
718 fn test_enum_unit_variant() {
719 let doc = to_document(&Animal::Dog).unwrap();
720 assert_eq!(doc, Document::String("Dog".to_owned()));
721 test_roundtrip(&[Animal::Dog]);
722 }
723
724 #[test]
725 fn test_enum_tuple_variant() {
726 let frog = Animal::Frog("Henry".to_owned(), vec![349, 102]);
727 let doc = to_document(&frog).unwrap();
728 assert!(doc.is_object());
729 let obj = doc.as_object().unwrap();
730 assert!(obj.contains_key("Frog"));
731 test_roundtrip(&[
732 Animal::Frog("Henry".to_owned(), vec![]),
733 Animal::Frog("Henry".to_owned(), vec![349, 102]),
734 ]);
735 }
736
737 #[test]
738 fn test_enum_struct_variant() {
739 let cat = Animal::Cat {
740 age: 5,
741 name: "Kate".to_owned(),
742 };
743 let doc = to_document(&cat).unwrap();
744 assert!(doc.is_object());
745 let obj = doc.as_object().unwrap();
746 assert!(obj.contains_key("Cat"));
747 test_roundtrip(&[cat]);
748 }
749
750 #[test]
751 fn test_enum_newtype_variant() {
752 let hive = Animal::AntHive(vec!["Bob".to_owned(), "Stuart".to_owned()]);
753 test_roundtrip(&[hive]);
754 }
755
756 #[test]
761 fn test_bytes() {
762 let data: &[u8] = &[1, 2, 3];
764 let doc = to_document(&data).unwrap();
765 assert_eq!(
766 doc,
767 Document::Array(vec![
768 Document::Number(Number::PosInt(1)),
769 Document::Number(Number::PosInt(2)),
770 Document::Number(Number::PosInt(3)),
771 ])
772 );
773
774 let empty: &[u8] = &[];
775 let doc = to_document(&empty).unwrap();
776 assert_eq!(doc, Document::Array(vec![]));
777 }
778
779 #[test]
784 fn test_deserialize_wrong_type() {
785 let result = from_document::<bool>(Document::String("not a bool".to_owned()));
786 assert!(result.is_err());
787 let err = result.unwrap_err();
788 assert!(
789 err.to_string().contains("invalid type"),
790 "unexpected error message: {}",
791 err
792 );
793 }
794
795 #[test]
796 fn test_deserialize_missing_field() {
797 #[derive(Debug, Deserialize)]
798 struct Required {
799 #[allow(dead_code)]
800 x: u32,
801 }
802
803 let doc = Document::Object(HashMap::new());
804 let result = from_document::<Required>(doc);
805 assert!(result.is_err());
806 let err = result.unwrap_err();
807 assert!(err.to_string().contains("missing field"));
808 }
809
810 #[test]
811 fn test_serialize_non_string_map_key_rejected() {
812 use std::collections::HashMap;
814 let mut map: HashMap<Option<u32>, u32> = HashMap::new();
815 map.insert(None, 1);
816
817 let result = to_document(&map);
818 assert!(result.is_err());
819 }
820
821 #[test]
826 fn test_serde_json_compatibility() {
827 let mut map: HashMap<String, Document> = HashMap::new();
828 map.insert("hello".into(), "world".to_string().into());
829 map.insert("pos_int".into(), Document::Number(Number::PosInt(1).into()));
830 map.insert(
831 "neg_int".into(),
832 Document::Number(Number::NegInt(-1).into()),
833 );
834 map.insert(
835 "float".into(),
836 Document::Number(Number::Float(0.1 + 0.2).into()),
837 );
838 map.insert("true".into(), true.into());
839 map.insert("false".into(), false.into());
840 map.insert(
841 "array".into(),
842 vec![
843 map.clone().into(),
844 "hello-world".to_string().into(),
845 true.into(),
846 false.into(),
847 ]
848 .into(),
849 );
850 map.insert("map".into(), map.clone().into());
851 map.insert("null".into(), Document::Null);
852 let obj = Document::Object(map);
853
854 let target_file = include_str!("../../test_data/serialize_document.json");
855 let json: Result<serde_json::Value, _> = serde_json::from_str(target_file);
856 assert_eq!(serde_json::to_value(&obj).unwrap(), json.unwrap());
857 let doc: Result<Document, _> = serde_json::from_str(target_file);
858 assert_eq!(obj, doc.unwrap());
859 }
860}