use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Scheme {
Http,
Https,
Ws,
Wss,
}
impl Default for Scheme {
fn default() -> Self {
Scheme::Http
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct Spec {
pub swagger: String,
pub info: Info,
#[serde(skip_serializing_if = "Option::is_none")]
pub host: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "basePath")]
pub base_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub schemes: Option<Vec<Scheme>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub consumes: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub produces: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<Tag>>,
pub paths: BTreeMap<String, PathItem>,
#[serde(skip_serializing_if = "Option::is_none")]
pub definitions: Option<BTreeMap<String, Schema>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Option<BTreeMap<String, Parameter>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub responses: Option<BTreeMap<String, Response>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub security_definitions: Option<BTreeMap<String, Security>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub security: Option<Vec<BTreeMap<String, Vec<String>>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub external_docs: Option<Vec<ExternalDoc>>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub struct Tag {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub external_docs: Option<Vec<ExternalDoc>>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct ExternalDoc {
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub struct Info {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(rename = "termsOfService", skip_serializing_if = "Option::is_none")]
pub terms_of_service: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub contact: Option<Contact>,
#[serde(skip_serializing_if = "Option::is_none")]
pub license: Option<License>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct Contact {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct License {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct PathItem {
#[serde(skip_serializing_if = "Option::is_none")]
pub get: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub post: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub put: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub patch: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delete: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub head: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Option<Vec<ParameterOrRef>>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub struct Operation {
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub consumes: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub produces: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub schemes: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<String>>,
#[serde(rename = "operationId", skip_serializing_if = "Option::is_none")]
pub operation_id: Option<String>,
pub responses: BTreeMap<String, Response>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Option<Vec<ParameterOrRef>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub security: Option<Vec<SecurityRequirement>>,
}
pub type SecurityRequirement = BTreeMap<String, Vec<String>>;
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct Parameter {
pub name: String,
#[serde(rename = "in")]
pub location: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub required: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub schema: Option<Schema>,
#[serde(skip_serializing_if = "Option::is_none")]
pub unique_items: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "type")]
pub param_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub items: Option<Schema>,
#[serde(skip_serializing_if = "Option::is_none")]
default: Option<serde_json::Value>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct Response {
pub description: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub schema: Option<Schema>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(untagged)]
pub enum ParameterOrRef {
Parameter {
name: String,
#[serde(rename = "in")]
location: String,
#[serde(skip_serializing_if = "Option::is_none")]
required: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
schema: Option<Schema>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "uniqueItems")]
unique_items: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "type")]
param_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
format: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[serde(rename = "collectionFormat", skip_serializing_if = "Option::is_none")]
collection_format: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
default: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
items: Option<Schema>,
#[serde(
rename = "additionalProperties",
skip_serializing_if = "Option::is_none"
)]
additional_properties: Option<Schema>,
},
Ref {
#[serde(rename = "$ref")]
ref_path: String,
},
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(tag = "type")]
pub enum Security {
#[serde(rename = "apiKey")]
ApiKey {
name: String,
#[serde(rename = "in")]
location: String,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
},
#[serde(rename = "oauth2")]
Oauth2 {
flow: Flow,
#[serde(rename = "authorizationUrl")]
authorization_url: String,
#[serde(rename = "tokenUrl")]
#[serde(skip_serializing_if = "Option::is_none")]
token_url: Option<String>,
scopes: BTreeMap<String, String>,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
},
#[serde(rename = "basic")]
Basic {
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
},
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum Flow {
Implicit,
Password,
Application,
AccessCode,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct Schema {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "$ref")]
pub ref_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "type")]
pub schema_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "enum")]
pub enum_values: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub required: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub items: Option<Box<Schema>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<BTreeMap<String, Schema>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "allOf")]
pub all_of: Option<Vec<Box<Schema>>>,
#[serde(flatten)]
pub other: BTreeMap<String, serde_json::Value>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
use serde_yaml;
use std::collections::BTreeMap;
#[test]
fn security_api_deserializes() {
let json = r#"{"type":"apiKey", "name":"foo", "in": "query"}"#;
assert_eq!(
serde_yaml::from_str::<Security>(&json).unwrap(),
Security::ApiKey {
name: "foo".into(),
location: "query".into(),
description: None,
}
);
}
#[test]
fn security_api_serializes() {
let json = r#"{"type":"apiKey","name":"foo","in":"query"}"#;
assert_eq!(
serde_json::to_string(&Security::ApiKey {
name: "foo".into(),
location: "query".into(),
description: None,
})
.unwrap(),
json
);
}
#[test]
fn security_basic_deserializes() {
let json = r#"{"type":"basic"}"#;
assert_eq!(
serde_yaml::from_str::<Security>(&json).unwrap(),
Security::Basic { description: None }
);
}
#[test]
fn security_basic_serializes() {
let json = r#"{"type":"basic"}"#;
assert_eq!(
json,
serde_json::to_string(&Security::Basic { description: None }).unwrap()
);
}
#[test]
fn security_oauth_deserializes() {
let json = r#"{"type":"oauth2","flow":"implicit","authorizationUrl":"foo/bar","scopes":{"foo":"bar"}}"#;
let mut scopes = BTreeMap::new();
scopes.insert("foo".into(), "bar".into());
assert_eq!(
serde_yaml::from_str::<Security>(&json).unwrap(),
Security::Oauth2 {
flow: Flow::Implicit,
authorization_url: "foo/bar".into(),
token_url: None,
scopes: scopes,
description: None,
}
);
}
#[test]
fn security_oauth_serializes() {
let json = r#"{"type":"oauth2","flow":"implicit","authorizationUrl":"foo/bar","scopes":{"foo":"bar"}}"#;
let mut scopes = BTreeMap::new();
scopes.insert("foo".into(), "bar".into());
assert_eq!(
json,
serde_json::to_string(&Security::Oauth2 {
flow: Flow::Implicit,
authorization_url: "foo/bar".into(),
token_url: None,
scopes: scopes,
description: None,
})
.unwrap()
);
}
#[test]
fn parameter_or_ref_deserializes_ref() {
let json = r#"{"$ref":"foo/bar"}"#;
assert_eq!(
serde_yaml::from_str::<ParameterOrRef>(&json).unwrap(),
ParameterOrRef::Ref {
ref_path: "foo/bar".into()
}
);
}
#[test]
fn parameter_or_ref_serializes_pref() {
let json = r#"{"$ref":"foo/bar"}"#;
assert_eq!(
json,
serde_json::to_string(&ParameterOrRef::Ref {
ref_path: "foo/bar".into()
},)
.unwrap()
);
}
}