1use serde::{Deserialize, Serialize};
2use std::collections::BTreeMap;
3
4#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
7#[serde(rename_all = "lowercase")]
8pub enum Scheme {
9 Http,
10 Https,
11 Ws,
12 Wss,
13}
14
15impl Default for Scheme {
16 fn default() -> Self {
17 Scheme::Http
18 }
19}
20
21#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
23#[serde(rename_all = "camelCase")]
24pub struct Spec {
25 pub swagger: String,
27 pub info: Info,
28 #[serde(skip_serializing_if = "Option::is_none")]
31 pub host: Option<String>,
32 #[serde(skip_serializing_if = "Option::is_none")]
34 #[serde(rename = "basePath")]
35 pub base_path: Option<String>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub schemes: Option<Vec<Scheme>>,
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub consumes: Option<Vec<String>>,
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub produces: Option<Vec<String>>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub tags: Option<Vec<Tag>>,
46 pub paths: BTreeMap<String, PathItem>,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub definitions: Option<BTreeMap<String, Schema>>,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub parameters: Option<BTreeMap<String, Parameter>>,
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub responses: Option<BTreeMap<String, Response>>,
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub security_definitions: Option<BTreeMap<String, Security>>,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub security: Option<Vec<BTreeMap<String, Vec<String>>>>,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub external_docs: Option<Vec<ExternalDoc>>,
62}
63
64#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
65#[serde(rename_all = "lowercase")]
66pub struct Tag {
67 pub name: String,
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub description: Option<String>,
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub external_docs: Option<Vec<ExternalDoc>>,
72}
73
74#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
75pub struct ExternalDoc {
76 pub url: String,
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub description: Option<String>,
79}
80
81#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
85#[serde(rename_all = "lowercase")]
86pub struct Info {
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub title: Option<String>,
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub description: Option<String>,
93 #[serde(rename = "termsOfService", skip_serializing_if = "Option::is_none")]
94 pub terms_of_service: Option<String>,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub contact: Option<Contact>,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub license: Option<License>,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub version: Option<String>,
101}
102
103#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
104pub struct Contact {
105 #[serde(skip_serializing_if = "Option::is_none")]
106 pub name: Option<String>,
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub url: Option<String>,
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub email: Option<String>,
113}
114
115#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
117pub struct License {
118 #[serde(skip_serializing_if = "Option::is_none")]
121 pub name: Option<String>,
122 #[serde(skip_serializing_if = "Option::is_none")]
125 pub url: Option<String>,
126}
127
128#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
130pub struct PathItem {
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub get: Option<Operation>,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub post: Option<Operation>,
135 #[serde(skip_serializing_if = "Option::is_none")]
136 pub put: Option<Operation>,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub patch: Option<Operation>,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub delete: Option<Operation>,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub options: Option<Operation>,
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub head: Option<Operation>,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub parameters: Option<Vec<ParameterOrRef>>,
147}
148
149#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
151#[serde(rename_all = "lowercase")]
152pub struct Operation {
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub summary: Option<String>,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub description: Option<String>,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub consumes: Option<Vec<String>>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub produces: Option<Vec<String>>,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub schemes: Option<Vec<String>>,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub tags: Option<Vec<String>>,
165 #[serde(rename = "operationId", skip_serializing_if = "Option::is_none")]
166 pub operation_id: Option<String>,
167 pub responses: BTreeMap<String, Response>,
168 #[serde(skip_serializing_if = "Option::is_none")]
169 pub parameters: Option<Vec<ParameterOrRef>>,
170 #[serde(skip_serializing_if = "Option::is_none")]
171 pub security: Option<Vec<SecurityRequirement>>,
172}
173
174pub type SecurityRequirement = BTreeMap<String, Vec<String>>;
176
177#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
178#[serde(rename_all = "camelCase")]
179pub struct Parameter {
180 pub name: String,
181 #[serde(rename = "in")]
182 pub location: String,
183 #[serde(skip_serializing_if = "Option::is_none")]
184 pub required: Option<bool>,
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub schema: Option<Schema>,
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub unique_items: Option<bool>,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 #[serde(rename = "type")]
191 pub param_type: Option<String>,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub format: Option<String>,
194 #[serde(skip_serializing_if = "Option::is_none")]
195 pub description: Option<String>,
196 #[serde(skip_serializing_if = "Option::is_none")]
197 pub items: Option<Schema>,
198 #[serde(skip_serializing_if = "Option::is_none")]
199 default: Option<serde_json::Value>,
200}
201
202#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
203pub struct Response {
204 pub description: String,
205 #[serde(skip_serializing_if = "Option::is_none")]
206 pub schema: Option<Schema>,
207}
208
209#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
212#[serde(untagged)]
213pub enum ParameterOrRef {
214 Parameter {
216 name: String,
218 #[serde(rename = "in")]
221 location: String,
222 #[serde(skip_serializing_if = "Option::is_none")]
223 required: Option<bool>,
224 #[serde(skip_serializing_if = "Option::is_none")]
225 schema: Box<Option<Schema>>,
226 #[serde(skip_serializing_if = "Option::is_none")]
227 #[serde(rename = "uniqueItems")]
228 unique_items: Option<bool>,
229 #[serde(skip_serializing_if = "Option::is_none")]
231 #[serde(rename = "type")]
232 param_type: Option<String>,
233 #[serde(skip_serializing_if = "Option::is_none")]
234 format: Option<String>,
235 #[serde(skip_serializing_if = "Option::is_none")]
238 description: Option<String>,
239 #[serde(rename = "collectionFormat", skip_serializing_if = "Option::is_none")]
240 collection_format: Option<String>,
241 #[serde(skip_serializing_if = "Option::is_none")]
242 default: Option<serde_json::Value>,
243 #[serde(skip_serializing_if = "Option::is_none")]
256 items: Box<Option<Schema>>,
257 #[serde(
258 rename = "additionalProperties",
259 skip_serializing_if = "Option::is_none"
260 )]
261 additional_properties: Box<Option<Schema>>,
262 },
263 Ref {
264 #[serde(rename = "$ref")]
265 ref_path: String,
266 },
267}
268#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
269#[serde(tag = "type")]
270pub enum Security {
271 #[serde(rename = "apiKey")]
272 ApiKey {
273 name: String,
274 #[serde(rename = "in")]
275 location: String,
276 #[serde(skip_serializing_if = "Option::is_none")]
277 description: Option<String>,
278 },
279 #[serde(rename = "oauth2")]
280 Oauth2 {
281 flow: Flow,
282 #[serde(rename = "authorizationUrl")]
283 authorization_url: String,
284 #[serde(rename = "tokenUrl")]
285 #[serde(skip_serializing_if = "Option::is_none")]
286 token_url: Option<String>,
287 scopes: BTreeMap<String, String>,
288 #[serde(skip_serializing_if = "Option::is_none")]
289 description: Option<String>,
290 },
291 #[serde(rename = "basic")]
292 Basic {
293 #[serde(skip_serializing_if = "Option::is_none")]
294 description: Option<String>,
295 },
296}
297
298#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
299#[serde(rename_all = "camelCase")]
300pub enum Flow {
301 Implicit,
302 Password,
303 Application,
304 AccessCode,
305}
306
307#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
312pub struct Schema {
313 #[serde(skip_serializing_if = "Option::is_none")]
314 #[serde(rename = "$ref")]
317 pub ref_path: Option<String>,
318 #[serde(skip_serializing_if = "Option::is_none")]
319 pub description: Option<String>,
320 #[serde(skip_serializing_if = "Option::is_none")]
321 #[serde(rename = "type")]
322 pub schema_type: Option<String>,
323 #[serde(skip_serializing_if = "Option::is_none")]
324 pub format: Option<String>,
325 #[serde(skip_serializing_if = "Option::is_none")]
326 #[serde(rename = "enum")]
327 pub enum_values: Option<Vec<String>>,
328 #[serde(skip_serializing_if = "Option::is_none")]
329 pub required: Option<Vec<String>>,
330 #[serde(skip_serializing_if = "Option::is_none")]
331 pub items: Option<Box<Schema>>,
332 #[serde(skip_serializing_if = "Option::is_none")]
334 pub properties: Option<BTreeMap<String, Schema>>,
335 #[serde(skip_serializing_if = "Option::is_none")]
337 #[serde(rename = "allOf")]
338 pub all_of: Option<Vec<Box<Schema>>>,
339 #[serde(flatten)]
341 pub other: BTreeMap<String, serde_json::Value>,
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347 use serde_json;
348 use serde_yaml;
349 use std::collections::BTreeMap;
350
351 #[test]
352 fn security_api_deserializes() {
353 let json = r#"{"type":"apiKey", "name":"foo", "in": "query"}"#;
354 assert_eq!(
355 serde_yaml::from_str::<Security>(&json).unwrap(),
356 Security::ApiKey {
357 name: "foo".into(),
358 location: "query".into(),
359 description: None,
360 }
361 );
362 }
363
364 #[test]
365 fn security_api_serializes() {
366 let json = r#"{"type":"apiKey","name":"foo","in":"query"}"#;
367 assert_eq!(
368 serde_json::to_string(&Security::ApiKey {
369 name: "foo".into(),
370 location: "query".into(),
371 description: None,
372 })
373 .unwrap(),
374 json
375 );
376 }
377
378 #[test]
379 fn security_basic_deserializes() {
380 let json = r#"{"type":"basic"}"#;
381 assert_eq!(
382 serde_yaml::from_str::<Security>(&json).unwrap(),
383 Security::Basic { description: None }
384 );
385 }
386
387 #[test]
388 fn security_basic_serializes() {
389 let json = r#"{"type":"basic"}"#;
390 assert_eq!(
391 json,
392 serde_json::to_string(&Security::Basic { description: None }).unwrap()
393 );
394 }
395
396 #[test]
397 fn security_oauth_deserializes() {
398 let json = r#"{"type":"oauth2","flow":"implicit","authorizationUrl":"foo/bar","scopes":{"foo":"bar"}}"#;
399 let mut scopes = BTreeMap::new();
400 scopes.insert("foo".into(), "bar".into());
401 assert_eq!(
402 serde_yaml::from_str::<Security>(&json).unwrap(),
403 Security::Oauth2 {
404 flow: Flow::Implicit,
405 authorization_url: "foo/bar".into(),
406 token_url: None,
407 scopes: scopes,
408 description: None,
409 }
410 );
411 }
412
413 #[test]
414 fn security_oauth_serializes() {
415 let json = r#"{"type":"oauth2","flow":"implicit","authorizationUrl":"foo/bar","scopes":{"foo":"bar"}}"#;
416 let mut scopes = BTreeMap::new();
417 scopes.insert("foo".into(), "bar".into());
418 assert_eq!(
419 json,
420 serde_json::to_string(&Security::Oauth2 {
421 flow: Flow::Implicit,
422 authorization_url: "foo/bar".into(),
423 token_url: None,
424 scopes: scopes,
425 description: None,
426 })
427 .unwrap()
428 );
429 }
430
431 #[test]
432 fn parameter_or_ref_deserializes_ref() {
433 let json = r#"{"$ref":"foo/bar"}"#;
434 assert_eq!(
435 serde_yaml::from_str::<ParameterOrRef>(&json).unwrap(),
436 ParameterOrRef::Ref {
437 ref_path: "foo/bar".into()
438 }
439 );
440 }
441
442 #[test]
443 fn parameter_or_ref_serializes_pref() {
444 let json = r#"{"$ref":"foo/bar"}"#;
445 assert_eq!(
446 json,
447 serde_json::to_string(&ParameterOrRef::Ref {
448 ref_path: "foo/bar".into()
449 },)
450 .unwrap()
451 );
452 }
453}