#![cfg(feature = "vespera")]
#![cfg_attr(docsrs, doc(cfg(feature = "vespera")))]
use http::StatusCode;
pub use vespera_core::Contact;
pub use vespera_core::Example;
pub use vespera_core::Header;
pub use vespera_core::HttpMethod;
pub use vespera_core::Info;
pub use vespera_core::License;
pub use vespera_core::MediaType;
pub use vespera_core::OpenApi;
pub use vespera_core::OpenApiVersion;
pub use vespera_core::Operation;
pub use vespera_core::Parameter;
pub use vespera_core::ParameterLocation;
pub use vespera_core::PathItem;
pub use vespera_core::RequestBody;
pub use vespera_core::Response as VesperaResponse;
pub use vespera_core::Schema;
pub use vespera_core::SchemaRef;
pub use vespera_core::SchemaType;
pub use vespera_core::Server;
pub use vespera_core::ServerVariable;
pub use vespera_core::Tag;
pub use vespera_core::openapi;
pub use vespera_core::route;
pub use vespera_core::schema;
use crate::body::TakoBody;
use crate::responder::Responder;
use crate::types::Response;
pub struct VesperaOpenApiJson(pub OpenApi);
impl Responder for VesperaOpenApiJson {
fn into_response(self) -> Response {
match serde_json::to_vec(&self.0) {
Ok(buf) => {
let mut res = Response::new(TakoBody::from(buf));
res.headers_mut().insert(
http::header::CONTENT_TYPE,
http::HeaderValue::from_static("application/json"),
);
res
}
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response(),
}
}
}
pub fn http_method_to_vespera(method: &http::Method) -> HttpMethod {
match method.as_str() {
"GET" => HttpMethod::Get,
"POST" => HttpMethod::Post,
"PUT" => HttpMethod::Put,
"PATCH" => HttpMethod::Patch,
"DELETE" => HttpMethod::Delete,
"HEAD" => HttpMethod::Head,
"OPTIONS" => HttpMethod::Options,
"TRACE" => HttpMethod::Trace,
_ => HttpMethod::Get,
}
}
pub fn parameter_location_to_vespera(loc: &super::ParameterLocation) -> ParameterLocation {
match loc {
super::ParameterLocation::Query => ParameterLocation::Query,
super::ParameterLocation::Header => ParameterLocation::Header,
super::ParameterLocation::Path => ParameterLocation::Path,
super::ParameterLocation::Cookie => ParameterLocation::Cookie,
}
}
pub fn route_openapi_to_operation(route: &super::RouteOpenApi) -> Operation {
use std::collections::BTreeMap;
let parameters: Vec<Parameter> = route
.parameters
.iter()
.map(|p| Parameter {
name: p.name.clone(),
r#in: parameter_location_to_vespera(&p.location),
description: p.description.clone(),
required: Some(p.required),
schema: None,
example: None,
})
.collect();
let responses: BTreeMap<String, VesperaResponse> = route
.responses
.iter()
.map(|(status, desc)| {
(
status.to_string(),
VesperaResponse {
description: desc.clone(),
headers: None,
content: None,
},
)
})
.collect();
let request_body = route.request_body.as_ref().map(|rb| {
let mut content = BTreeMap::new();
let schema = if rb.schema_properties.is_empty() {
None
} else {
let mut properties = BTreeMap::new();
for prop in &rb.schema_properties {
properties.insert(
prop.name.clone(),
SchemaRef::Inline(Box::new(Schema {
schema_type: Some(match prop.property_type.as_str() {
"integer" => SchemaType::Integer,
"number" => SchemaType::Number,
"boolean" => SchemaType::Boolean,
"array" => SchemaType::Array,
"object" => SchemaType::Object,
_ => SchemaType::String,
}),
description: prop.description.clone(),
..Default::default()
})),
);
}
Some(SchemaRef::Inline(Box::new(Schema {
schema_type: Some(SchemaType::Object),
properties: Some(properties),
..Default::default()
})))
};
content.insert(
rb.content_type.clone(),
MediaType {
schema,
example: None,
examples: None,
},
);
RequestBody {
description: rb.description.clone(),
required: Some(rb.required),
content,
}
});
let security = if route.security.is_empty() {
None
} else {
Some(
route
.security
.iter()
.map(|s| {
let mut map = std::collections::HashMap::new();
map.insert(s.clone(), vec![]);
map
})
.collect(),
)
};
Operation {
operation_id: route.operation_id.clone(),
tags: if route.tags.is_empty() {
None
} else {
Some(route.tags.clone())
},
summary: route.summary.clone(),
description: route.description.clone(),
parameters: if parameters.is_empty() {
None
} else {
Some(parameters)
},
request_body,
responses,
security,
}
}
pub fn generate_openapi_from_routes(router: &crate::router::Router, info: Info) -> OpenApi {
use std::collections::BTreeMap;
let routes = router.collect_openapi_routes();
let mut paths: BTreeMap<String, PathItem> = BTreeMap::new();
for (method, path, route_openapi) in routes {
let operation = route_openapi_to_operation(&route_openapi);
let vespera_method = http_method_to_vespera(&method);
let path_item = paths.entry(path).or_insert_with(|| PathItem {
get: None,
post: None,
put: None,
patch: None,
delete: None,
head: None,
options: None,
trace: None,
parameters: None,
summary: None,
description: None,
});
path_item.set_operation(vespera_method, operation);
}
OpenApi {
openapi: OpenApiVersion::default(),
info,
servers: None,
paths,
components: None,
security: None,
tags: None,
external_docs: None,
}
}