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 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 }
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 }
103
104#[derive(Debug, Deserialize)]
105pub struct ServiceOperation {
106 }
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 traits: Traits,
131}
132
133#[derive(Debug, Deserialize)]
134pub struct BlobShape {
135 }
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 }
160
161#[derive(Debug, Deserialize)]
162pub struct MapValue {
163 pub target: String,
164 }
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 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 #[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 #[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}