aws_smithy_types/
document.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6use crate::Number;
7use std::borrow::Cow;
8use std::collections::HashMap;
9
10#[cfg(any(
11    all(aws_sdk_unstable, feature = "serde-deserialize"),
12    all(aws_sdk_unstable, feature = "serde-serialize")
13))]
14use serde;
15
16/* ANCHOR: document */
17
18/// Document Type
19///
20/// Document types represents protocol-agnostic open content that is accessed like JSON data.
21/// Open content is useful for modeling unstructured data that has no schema, data that can't be
22/// modeled using rigid types, or data that has a schema that evolves outside of the purview of a model.
23/// The serialization format of a document is an implementation detail of a protocol.
24#[derive(Clone, Debug, PartialEq)]
25#[cfg_attr(
26    all(aws_sdk_unstable, feature = "serde-serialize"),
27    derive(serde::Serialize)
28)]
29#[cfg_attr(
30    all(aws_sdk_unstable, feature = "serde-deserialize"),
31    derive(serde::Deserialize)
32)]
33#[cfg_attr(
34    any(
35        all(aws_sdk_unstable, feature = "serde-deserialize"),
36        all(aws_sdk_unstable, feature = "serde-serialize")
37    ),
38    serde(untagged)
39)]
40pub enum Document {
41    /// JSON object
42    Object(HashMap<String, Document>),
43    /// JSON array
44    Array(Vec<Document>),
45    /// JSON number
46    Number(Number),
47    /// JSON string
48    String(String),
49    /// JSON boolean
50    Bool(bool),
51    /// JSON null
52    Null,
53}
54
55impl Document {
56    /// Returns the inner map value if this `Document` is an object.
57    pub fn as_object(&self) -> Option<&HashMap<String, Document>> {
58        if let Self::Object(object) = self {
59            Some(object)
60        } else {
61            None
62        }
63    }
64
65    /// Returns the mutable inner map value if this `Document` is an object.
66    pub fn as_object_mut(&mut self) -> Option<&mut HashMap<String, Document>> {
67        if let Self::Object(object) = self {
68            Some(object)
69        } else {
70            None
71        }
72    }
73
74    /// Returns the inner array value if this `Document` is an array.
75    pub fn as_array(&self) -> Option<&Vec<Document>> {
76        if let Self::Array(array) = self {
77            Some(array)
78        } else {
79            None
80        }
81    }
82
83    /// Returns the mutable inner array value if this `Document` is an array.
84    pub fn as_array_mut(&mut self) -> Option<&mut Vec<Document>> {
85        if let Self::Array(array) = self {
86            Some(array)
87        } else {
88            None
89        }
90    }
91
92    /// Returns the inner number value if this `Document` is a number.
93    pub fn as_number(&self) -> Option<&Number> {
94        if let Self::Number(number) = self {
95            Some(number)
96        } else {
97            None
98        }
99    }
100
101    /// Returns the inner string value if this `Document` is a string.
102    pub fn as_string(&self) -> Option<&str> {
103        if let Self::String(string) = self {
104            Some(string)
105        } else {
106            None
107        }
108    }
109
110    /// Returns the inner boolean value if this `Document` is a boolean.
111    pub fn as_bool(&self) -> Option<bool> {
112        if let Self::Bool(boolean) = self {
113            Some(*boolean)
114        } else {
115            None
116        }
117    }
118
119    /// Returns `Some(())` if this `Document` is a null.
120    pub fn as_null(&self) -> Option<()> {
121        if let Self::Null = self {
122            Some(())
123        } else {
124            None
125        }
126    }
127
128    /// Returns `true` if this `Document` is an object.
129    pub fn is_object(&self) -> bool {
130        matches!(self, Self::Object(_))
131    }
132
133    /// Returns `true` if this `Document` is an array.
134    pub fn is_array(&self) -> bool {
135        matches!(self, Self::Array(_))
136    }
137
138    /// Returns `true` if this `Document` is a number.
139    pub fn is_number(&self) -> bool {
140        matches!(self, Self::Number(_))
141    }
142
143    /// Returns `true` if this `Document` is a string.
144    pub fn is_string(&self) -> bool {
145        matches!(self, Self::String(_))
146    }
147
148    /// Returns `true` if this `Document` is a bool.
149    pub fn is_bool(&self) -> bool {
150        matches!(self, Self::Bool(_))
151    }
152
153    /// Returns `true` if this `Document` is a boolean.
154    pub fn is_null(&self) -> bool {
155        matches!(self, Self::Null)
156    }
157}
158
159/// The default value is `Document::Null`.
160impl Default for Document {
161    fn default() -> Self {
162        Self::Null
163    }
164}
165
166impl From<bool> for Document {
167    fn from(value: bool) -> Self {
168        Document::Bool(value)
169    }
170}
171
172impl<'a> From<&'a str> for Document {
173    fn from(value: &'a str) -> Self {
174        Document::String(value.to_string())
175    }
176}
177
178impl<'a> From<Cow<'a, str>> for Document {
179    fn from(value: Cow<'a, str>) -> Self {
180        Document::String(value.into_owned())
181    }
182}
183
184impl From<String> for Document {
185    fn from(value: String) -> Self {
186        Document::String(value)
187    }
188}
189
190impl From<Vec<Document>> for Document {
191    fn from(values: Vec<Document>) -> Self {
192        Document::Array(values)
193    }
194}
195
196impl From<HashMap<String, Document>> for Document {
197    fn from(values: HashMap<String, Document>) -> Self {
198        Document::Object(values)
199    }
200}
201
202impl From<u64> for Document {
203    fn from(value: u64) -> Self {
204        Document::Number(Number::PosInt(value))
205    }
206}
207
208impl From<i64> for Document {
209    fn from(value: i64) -> Self {
210        Document::Number(Number::NegInt(value))
211    }
212}
213
214impl From<i32> for Document {
215    fn from(value: i32) -> Self {
216        Document::Number(Number::NegInt(value as i64))
217    }
218}
219
220impl From<f64> for Document {
221    fn from(value: f64) -> Self {
222        Document::Number(Number::Float(value))
223    }
224}
225
226impl From<Number> for Document {
227    fn from(value: Number) -> Self {
228        Document::Number(value)
229    }
230}
231
232/* ANCHOR END: document */
233
234#[cfg(test)]
235mod test {
236    /// checks if a) serialization of json suceeds and b) it is compatible with serde_json
237    #[test]
238    #[cfg(all(
239        aws_sdk_unstable,
240        feature = "serde-serialize",
241        feature = "serde-deserialize"
242    ))]
243    fn serialize_json() {
244        use crate::Document;
245        use crate::Number;
246        use std::collections::HashMap;
247        let mut map: HashMap<String, Document> = HashMap::new();
248        // string
249        map.insert("hello".into(), "world".to_string().into());
250        // numbers
251        map.insert("pos_int".into(), Document::Number(Number::PosInt(1).into()));
252        map.insert(
253            "neg_int".into(),
254            Document::Number(Number::NegInt(-1).into()),
255        );
256        map.insert(
257            "float".into(),
258            Document::Number(Number::Float(0.1 + 0.2).into()),
259        );
260        // booleans
261        map.insert("true".into(), true.into());
262        map.insert("false".into(), false.into());
263        // check if array with different datatypes would succeed
264        map.insert(
265            "array".into(),
266            vec![
267                map.clone().into(),
268                "hello-world".to_string().into(),
269                true.into(),
270                false.into(),
271            ]
272            .into(),
273        );
274        // map
275        map.insert("map".into(), map.clone().into());
276        // null
277        map.insert("null".into(), Document::Null);
278        let obj = Document::Object(map);
279        // comparing string isnt going to work since there is no gurantee for the ordering of the keys
280        let target_file = include_str!("../test_data/serialize_document.json");
281        let json: Result<serde_json::Value, _> = serde_json::from_str(target_file);
282        // serializer
283        assert_eq!(serde_json::to_value(&obj).unwrap(), json.unwrap());
284        let doc: Result<Document, _> = serde_json::from_str(target_file);
285        assert_eq!(obj, doc.unwrap());
286    }
287}