Skip to main content

s3s_model/
smithy.rs

1use std::collections::BTreeMap;
2
3use anyhow::{Result, ensure};
4use numeric_cast::numeric_cast;
5use serde::Deserialize;
6use serde_json::{Map, Value};
7
8#[derive(Debug, Deserialize)]
9pub struct Model {
10    pub smithy: String,
11    pub shapes: BTreeMap<String, Shape>,
12}
13
14#[derive(Debug, Deserialize)]
15#[serde(tag = "type", rename_all = "lowercase")]
16pub enum Shape {
17    Boolean(BooleanShape),
18    Integer(IntegerShape),
19    Long(LongShape),
20    String(StringShape),
21    Timestamp(TimestampShape),
22    Blob(BlobShape),
23    List(ListShape),
24    Map(MapShape),
25    Enum(EnumShape),
26    Union(UnionShape),
27    Structure(StructureShape),
28    Operation(OperationShape),
29    Service(ServiceShape),
30}
31
32#[derive(Debug, Deserialize, Default)]
33#[serde(transparent)]
34pub struct Traits(Option<Map<String, Value>>);
35
36#[derive(Debug, Deserialize)]
37pub struct StringShape {
38    pub traits: Traits,
39}
40
41#[derive(Debug, Deserialize)]
42pub struct TimestampShape {
43    pub traits: Traits,
44}
45
46#[derive(Debug, Deserialize)]
47pub struct StructureShape {
48    pub members: BTreeMap<String, StructureMember>,
49    pub traits: Traits,
50}
51
52#[derive(Debug, Deserialize)]
53pub struct StructureMember {
54    pub target: String,
55    pub traits: Traits,
56}
57
58#[derive(Debug, Deserialize)]
59pub struct OperationShape {
60    pub input: OperationInput,
61    pub output: OperationOutput,
62    // pub errors: Option<Vec<OperationError>>,
63    pub traits: Traits,
64}
65
66#[derive(Debug, Deserialize)]
67pub struct OperationInput {
68    pub target: String,
69}
70
71#[derive(Debug, Deserialize)]
72pub struct OperationOutput {
73    pub target: String,
74}
75
76#[derive(Debug, Deserialize)]
77pub struct OperationError {
78    // pub target: String,
79}
80
81#[derive(Debug, Deserialize)]
82pub struct BooleanShape {
83    pub traits: Traits,
84}
85
86#[derive(Debug, Deserialize)]
87pub struct ListShape {
88    pub member: ListMember,
89    pub traits: Traits,
90}
91
92#[derive(Debug, Deserialize)]
93pub struct ListMember {
94    pub target: String,
95    pub traits: Traits,
96}
97
98#[derive(Debug, Deserialize)]
99pub struct ServiceShape {
100    // pub version: String,
101    // pub operations: Vec<ServiceOperation>,
102}
103
104#[derive(Debug, Deserialize)]
105pub struct ServiceOperation {
106    // pub target: String,
107}
108
109#[derive(Debug, Deserialize)]
110pub struct UnionShape {
111    pub members: BTreeMap<String, UnionMember>,
112    pub traits: Traits,
113}
114
115#[derive(Debug, Deserialize)]
116pub struct UnionMember {
117    pub target: String,
118    pub traits: Traits,
119}
120
121#[derive(Debug, Deserialize)]
122pub struct EnumShape {
123    pub members: BTreeMap<String, EnumMember>,
124    pub traits: Traits,
125}
126
127#[derive(Debug, Deserialize)]
128pub struct EnumMember {
129    // pub target: String,
130    pub traits: Traits,
131}
132
133#[derive(Debug, Deserialize)]
134pub struct BlobShape {
135    // pub traits: Traits,
136}
137
138#[derive(Debug, Deserialize)]
139pub struct LongShape {
140    pub traits: Traits,
141}
142
143#[derive(Debug, Deserialize)]
144pub struct IntegerShape {
145    pub traits: Traits,
146}
147
148#[derive(Debug, Deserialize)]
149pub struct MapShape {
150    pub key: MapKey,
151    pub value: MapValue,
152    pub traits: Traits,
153}
154
155#[derive(Debug, Deserialize)]
156pub struct MapKey {
157    pub target: String,
158    // pub traits: Traits,
159}
160
161#[derive(Debug, Deserialize)]
162pub struct MapValue {
163    pub target: String,
164    // pub traits: Traits,
165}
166
167impl Model {
168    pub fn load_json(path: &str) -> Result<Self> {
169        let model: Self = serde_json::from_reader(std::fs::File::open(path)?)?;
170        ensure!(model.smithy == "2.0");
171        Ok(model)
172    }
173}
174
175impl Traits {
176    pub fn set(&mut self, key: &str, value: Value) {
177        let map = self.0.get_or_insert_with(Map::new);
178        map.insert(key.to_owned(), value);
179    }
180
181    #[must_use]
182    pub fn get(&self, key: &str) -> Option<&Value> {
183        let map = self.0.as_ref()?;
184        map.get(key)
185    }
186
187    /// Returns an iterator over the trait entries.
188    pub fn iter(&self) -> impl Iterator<Item = (&String, &Value)> {
189        self.0.as_ref().into_iter().flat_map(|m| m.iter())
190    }
191
192    #[must_use]
193    pub fn enum_value(&self) -> Option<&str> {
194        self.get("smithy.api#enumValue")?.as_str()
195    }
196
197    #[must_use]
198    pub fn doc(&self) -> Option<&str> {
199        self.get("smithy.api#documentation")?.as_str()
200    }
201
202    #[must_use]
203    pub fn timestamp_format(&self) -> Option<&str> {
204        self.get("smithy.api#timestampFormat")?.as_str()
205    }
206
207    #[must_use]
208    pub fn required(&self) -> bool {
209        self.get("smithy.api#required").is_some()
210    }
211
212    #[must_use]
213    pub fn default_value(&self) -> Option<&Value> {
214        self.get("smithy.api#default")
215    }
216
217    #[must_use]
218    pub fn http_header(&self) -> Option<&str> {
219        self.get("smithy.api#httpHeader")?.as_str()
220    }
221
222    #[must_use]
223    pub fn http_payload(&self) -> bool {
224        self.get("smithy.api#httpPayload").is_some()
225    }
226
227    #[must_use]
228    pub fn http_query(&self) -> Option<&str> {
229        self.get("smithy.api#httpQuery")?.as_str()
230    }
231
232    #[must_use]
233    pub fn xml_name(&self) -> Option<&str> {
234        self.get("smithy.api#xmlName")?.as_str()
235    }
236
237    /// Returns the alternative XML root element names (`s3s#xmlAltName` trait).
238    #[must_use]
239    pub fn xml_alt_names(&self) -> Vec<&str> {
240        self.get("s3s#xmlAltName")
241            .and_then(|v| v.as_array())
242            .map(|arr| arr.iter().filter_map(|v| v.as_str()).collect())
243            .unwrap_or_default()
244    }
245
246    /// Returns the body literal value (`s3s#bodyLiteral` trait).
247    #[must_use]
248    pub fn body_literal(&self) -> Option<&str> {
249        self.get("s3s#bodyLiteral")?.as_str()
250    }
251
252    #[must_use]
253    pub fn xml_attr(&self) -> bool {
254        self.get("smithy.api#xmlAttribute").is_some()
255    }
256
257    #[must_use]
258    pub fn xml_flattened(&self) -> bool {
259        self.get("smithy.api#xmlFlattened").is_some()
260    }
261
262    #[must_use]
263    fn xml_namespace(&self) -> Option<&Map<String, Value>> {
264        self.get("smithy.api#xmlNamespace")?.as_object()
265    }
266
267    #[must_use]
268    pub fn xml_namespace_uri(&self) -> Option<&str> {
269        self.xml_namespace()?.get("uri")?.as_str()
270    }
271
272    #[must_use]
273    pub fn xml_namespace_prefix(&self) -> Option<&str> {
274        self.xml_namespace()?.get("prefix")?.as_str()
275    }
276
277    #[must_use]
278    pub fn s3_unwrapped_xml_output(&self) -> bool {
279        self.get("aws.customizations#s3UnwrappedXmlOutput").is_some()
280    }
281
282    #[must_use]
283    pub fn http_label(&self) -> Option<&Value> {
284        self.get("smithy.api#httpLabel")
285    }
286
287    #[must_use]
288    pub fn http_prefix_headers(&self) -> Option<&str> {
289        self.get("smithy.api#httpPrefixHeaders")?.as_str()
290    }
291
292    #[must_use]
293    pub fn http_method(&self) -> Option<&str> {
294        self.get("smithy.api#http")?.as_object()?.get("method")?.as_str()
295    }
296
297    #[must_use]
298    pub fn http_uri(&self) -> Option<&str> {
299        self.get("smithy.api#http")?.as_object()?.get("uri")?.as_str()
300    }
301
302    pub fn http_code(&self) -> Option<u16> {
303        self.get("smithy.api#http")?
304            .as_object()?
305            .get("code")?
306            .as_u64()
307            .map(numeric_cast::<_, u16>)
308    }
309
310    #[must_use]
311    pub fn error(&self) -> Option<&str> {
312        self.get("smithy.api#error")?.as_str()
313    }
314}