use std::collections::HashMap;
use json::{object, JsonValue};
use crate::action::Action;
#[derive(Clone)]
pub struct Swagger {
openapi: String,
info: Info,
servers: Vec<Server>,
components: JsonValue,
tags: HashMap<String, Tag>,
security: Vec<JsonValue>,
paths: HashMap<String, HashMap<String, Api>>,
}
impl Swagger {
pub fn new(version: &str, title: &str, description: &str) -> Swagger {
Swagger {
openapi: "3.0.0".to_string(),
info: Info {
title: title.to_string(),
description: description.to_string(),
version: version.to_string(),
},
servers: vec![],
components: object! {},
tags: Default::default(),
security: vec![],
paths: Default::default(),
}
}
pub fn add_server(&mut self, url: &str, description: &str) {
self.servers.push(Server { url: url.to_string(), description: description.to_string() })
}
pub fn add_components_bearer_token(&mut self) {
self.components["securitySchemes"]["BearerToken"] = object! {
"type": "http",
"scheme": "bearer",
"bearerFormat": "Token"
};
self.security.push(object! {
"BearerToken":[]
})
}
pub fn add_authorization_header(&mut self, token: &str) {
self.components["parameters"]["AuthorizationHeader"] = object! {
"name": "Authorization",
"in": "header",
"required": true,
"description": "Bearer token for authentication",
"schema":{
"type": "string",
"example":format!("Bearer {}",token)
}
};
}
pub fn add_tags(&mut self, name: &str, description: &str) {
self.tags.insert(name.to_string(), Tag { name: name.to_string(), description: description.to_string() });
}
pub fn add_paths(&mut self, mut action: Box<dyn Action>) {
let path = format!("/{}/{}", action.version(), action.api().replace(".", "/"));
let mut t = HashMap::new();
t.insert(action.method().str().to_lowercase().clone(), Api::new(action));
self.paths.insert(path, t);
}
pub fn json(&mut self) -> JsonValue {
let mut paths = HashMap::new();
for (key, value) in self.paths.iter_mut() {
let mut t = HashMap::new();
for (x, y) in value.iter_mut() {
t.insert(x.clone(), y.json().clone());
}
paths.insert(key.clone(), t.clone());
}
object! {
openapi: self.openapi.clone(),
info: self.info.json(),
servers: self.servers.iter().map(|x|x.json()).collect::<Vec<JsonValue>>().clone(),
components: self.components.clone(),
security:self.security.clone(),
tags: self.tags.values().map(|y| y.json()).collect::<Vec<JsonValue>>().clone(),
paths: paths.clone()
}
}
}
#[derive(Clone)]
struct Info {
title: String,
description: String,
version: String,
}
impl Info {
pub fn json(&self) -> JsonValue {
object! {
title: self.title.clone(),
description: self.description.clone(),
version: self.version.clone()
}
}
}
#[derive(Clone)]
struct Server {
url: String,
description: String,
}
impl Server {
pub fn json(&self) -> JsonValue {
object! {
url: self.url.clone(),
description: self.description.clone(),
}
}
}
#[derive(Clone)]
struct Tag {
name: String,
description: String,
}
impl Tag {
pub fn json(&self) -> JsonValue {
object! {
name: self.name.clone(),
description: self.description.clone(),
}
}
}
#[derive(Clone)]
struct Api {
tags: Vec<String>,
summary: String,
description: String,
request_body: RequestBody,
responses: JsonValue,
}
impl Api {
pub fn new(mut action: Box<dyn Action>) -> Api {
let mut t = Self {
tags: vec![action.api().split(".").next().unwrap().to_string()],
summary: action.title(),
description: action.description(),
request_body: RequestBody::new(),
responses: object! {},
};
if !action.params().is_empty() {
t.request_body.set_required(true);
t.request_body.set_content(action.content_type().str().as_str(), action.params());
}
t
}
pub fn json(&self) -> JsonValue {
let mut t = object! {
tags:self.tags.clone(),
summary: self.summary.clone(),
description: self.description.clone(),
requestBody: self.request_body.json(),
responses: self.responses.clone(),
};
if !t["requestBody"]["required"].as_bool().unwrap() {
t.remove("requestBody");
}
t
}
}
#[derive(Clone)]
struct RequestBody {
required: bool,
content: JsonValue,
}
impl RequestBody {
pub fn new() -> RequestBody {
Self { required: false, content: object! {} }
}
pub fn set_required(&mut self, state: bool) {
self.required = state;
}
pub fn set_content(&mut self, content_type: &str, params: JsonValue) {
self.content[content_type] = object! {
schema:object! {"type":if params.is_array() {"array"}else{"object"}}
};
match self.content[content_type]["schema"]["type"].as_str().unwrap_or("") {
"object" => RequestBody::set_schema_object(&mut self.content[content_type]["schema"], params.clone()),
"array" => RequestBody::set_schema_array(&mut self.content[content_type]["schema"], params.clone()),
_ => {}
}
for (field, data) in params.entries() {
self.content[content_type]["example"][field] = data["example"].clone();
}
}
fn set_schema_object(data: &mut JsonValue, params: JsonValue) {
for (key, value) in params.entries() {
data["properties"][key]["type"] = RequestBody::mode(value["mode"].as_str().unwrap_or(""));
match data["properties"][key]["type"].as_str().unwrap_or("") {
"object" => RequestBody::set_sub_object(&mut data["properties"][key], value["items"].clone()),
"array" => RequestBody::set_sub_array(&mut data["properties"][key], value["items"].clone()),
_ => RequestBody::set_schema_str(&mut data["properties"][key], value.clone()),
}
}
}
fn set_schema_str(data: &mut JsonValue, params: JsonValue) {
data["type"] = RequestBody::mode(params["mode"].as_str().unwrap_or(""));
data["example"] = params["example"].clone();
}
fn set_schema_array(data: &mut JsonValue, params: JsonValue) {
data["items"] = params;
}
fn set_sub_array(data: &mut JsonValue, params: JsonValue) {
data["items"]["type"] = params["mode"].as_str().unwrap_or("string").into();
}
fn set_sub_object(data: &mut JsonValue, params: JsonValue) {
for (key, value) in params.entries() {
data["properties"][key]["type"] = RequestBody::mode(value["mode"].as_str().unwrap_or(""));
match data["properties"][key]["type"].as_str().unwrap_or("") {
"object" => RequestBody::set_sub_object(&mut data["properties"][key], value["items"].clone()),
"array" => RequestBody::set_sub_array(&mut data["properties"][key], value["items"].clone()),
_ => RequestBody::set_schema_str(&mut data["properties"][key], value.clone()),
}
}
}
fn mode(name: &str) -> JsonValue {
match name {
"array" => "array",
"int" => "integer",
"switch" => "boolean",
"radio" => "string",
_ => name
}.into()
}
pub fn json(&self) -> JsonValue {
object! {
required: self.required,
content: self.content.clone()
}
}
}