mod openapi;
pub mod rapidoc;
pub mod redoc;
pub mod scalar;
pub mod status;
pub mod swagger_ui;
pub use hypers_openapi_macro::{self, openapi, ToParameter, ToResponse, ToResponses, ToSchema};
pub use openapi::*;
pub use rapidoc::RapiDoc;
pub use redoc::ReDoc;
pub use scalar::Scalar;
pub use serde_json;
pub use status::StatusError;
pub use swagger_ui::{Config, OauthConfig, SwaggerUi, Url};
use hypers_core::{
prelude::{CookieParam, Form, Json, Path, Query},
FilePart, FileParts,
};
use hypers_openapi_macro::schema;
use serde::Deserialize;
use std::{
any::type_name,
collections::{BTreeMap, HashMap, LinkedList},
marker::PhantomData,
};
extern crate self as hypers_openapi;
pub mod oapi {
pub use super::*;
}
pub trait ToSchema {
fn to_schema(components: &mut Components) -> RefOr<schema::Schema>;
}
impl ToSchema for () {
fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
schema::empty().into()
}
}
macro_rules! impl_to_schema {
($ty:path) => {
impl_to_schema!( @impl_schema $ty );
};
(&$ty:path) => {
impl_to_schema!( @impl_schema &$ty );
};
(@impl_schema $($tt:tt)*) => {
impl ToSchema for $($tt)* {
fn to_schema(_components: &mut Components) -> crate::RefOr<crate::schema::Schema> {
schema!( $($tt)* ).into()
}
}
};
}
macro_rules! impl_to_schema_primitive {
($($tt:path),*) => {
$( impl_to_schema!( $tt ); )*
};
}
#[rustfmt::skip]
impl_to_schema_primitive!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, bool, f32, f64, String, str, char
);
impl_to_schema!(&str);
#[cfg(feature = "chrono")]
impl_to_schema_primitive!(chrono::NaiveDate, chrono::Duration, chrono::NaiveDateTime);
#[cfg(feature = "chrono")]
impl<T: chrono::TimeZone> ToSchema for chrono::DateTime<T> {
fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
schema!(#[inline] DateTime<T>).into()
}
}
#[cfg(any(feature = "decimal", feature = "decimal-float"))]
impl_to_schema!(rust_decimal::Decimal);
#[cfg(feature = "url")]
impl_to_schema!(url::Url);
#[cfg(feature = "uuid")]
impl_to_schema!(uuid::Uuid);
#[cfg(feature = "ulid")]
impl_to_schema!(ulid::Ulid);
#[cfg(feature = "time")]
impl_to_schema_primitive!(
time::Date,
time::PrimitiveDateTime,
time::OffsetDateTime,
time::Duration
);
#[cfg(feature = "smallvec")]
impl<T: ToSchema + smallvec::Array> ToSchema for smallvec::SmallVec<T> {
fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
schema!(#[inline] smallvec::SmallVec<T>).into()
}
}
impl<T: ToSchema> ToSchema for Vec<T> {
fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
schema!(#[inline] Vec<T>).into()
}
}
impl<T: ToSchema> ToSchema for LinkedList<T> {
fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
schema!(#[inline] LinkedList<T>).into()
}
}
impl<T: ToSchema> ToSchema for [T] {
fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
schema!(
#[inline]
[T]
)
.into()
}
}
impl<T: ToSchema, const N: usize> ToSchema for [T; N] {
fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
schema!(
#[inline]
[T; N]
)
.into()
}
}
impl<T: ToSchema> ToSchema for &[T] {
fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
schema!(
#[inline]
&[T]
)
.into()
}
}
impl<T: ToSchema> ToSchema for Option<T> {
fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
schema!(#[inline] Option<T>).into()
}
}
impl<T> ToSchema for PhantomData<T> {
fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
Schema::Object(Object::default()).into()
}
}
impl<K: ToSchema, V: ToSchema> ToSchema for BTreeMap<K, V> {
fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
schema!(#[inline]BTreeMap<K, V>).into()
}
}
impl<K: ToSchema, V: ToSchema> ToSchema for HashMap<K, V> {
fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
schema!(#[inline]HashMap<K, V>).into()
}
}
impl<T, E> ToSchema for Result<T, E>
where
T: ToSchema,
E: ToSchema,
{
#[inline]
fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
let symbol = type_name::<Self>().replace("::", ".");
let schema = schema::OneOf::new()
.item(T::to_schema(components))
.item(E::to_schema(components));
components.schemas.insert(symbol.clone(), schema.into());
crate::RefOr::Ref(crate::Ref::new(format!("#/components/schemas/{}", symbol)))
}
}
impl ToSchema for serde_json::Value {
#[inline]
fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
Schema::Object(Object::default()).into()
}
}
impl ToSchema for serde_json::Map<String, serde_json::Value> {
#[inline]
fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
schema!(#[inline]HashMap<K, V>).into()
}
}
pub trait ToParameter {
fn to_parameters(components: &mut Components) -> Parameters;
}
pub trait ToRequestBody {
fn to_request_body(components: &mut Components) -> RequestBody;
}
pub trait ToResponses {
fn to_responses(components: &mut Components) -> Responses;
}
pub trait ToResponse {
fn to_response(components: &mut Components) -> RefOr<crate::Response>;
}
pub trait HandlerArg {
fn register(components: &mut Components, operation: &mut Operation, arg: &str);
}
pub trait HandlerOut {
fn register(components: &mut Components, operation: &mut Operation);
}
impl<T, E> HandlerOut for Result<T, E>
where
T: HandlerOut + Send,
E: HandlerOut + Send,
{
#[inline]
fn register(components: &mut Components, operation: &mut Operation) {
T::register(components, operation);
E::register(components, operation);
}
}
impl<E> HandlerOut for Result<(), E>
where
E: HandlerOut + Send,
{
#[inline]
fn register(components: &mut Components, operation: &mut Operation) {
operation.responses.insert("200", Response::new("Ok"));
E::register(components, operation);
}
}
impl HandlerOut for &'static str {
#[inline]
fn register(components: &mut Components, operation: &mut Operation) {
operation.responses.insert(
"200",
Response::new("Ok").add_content("text/plain", String::to_schema(components)),
);
}
}
impl HandlerOut for String {
#[inline]
fn register(components: &mut Components, operation: &mut Operation) {
operation.responses.insert(
"200",
Response::new("Ok").add_content("text/plain", String::to_schema(components)),
);
}
}
impl<'a> HandlerOut for &'a String {
#[inline]
fn register(components: &mut Components, operation: &mut Operation) {
operation.responses.insert(
"200",
Response::new("Ok").add_content("text/plain", String::to_schema(components)),
);
}
}
impl<C> HandlerOut for Json<C>
where
C: ToSchema,
{
#[inline]
fn register(components: &mut Components, operation: &mut Operation) {
operation
.responses
.insert("200", Self::to_response(components));
}
}
impl HandlerOut for hypers_core::prelude::Response {
#[inline]
fn register(_: &mut Components, _: &mut Operation) {}
}
impl<C> ToResponses for Json<C>
where
C: ToSchema,
{
#[inline]
fn to_responses(components: &mut Components) -> Responses {
Responses::new().response(
"200",
Response::new("Response json format data")
.add_content("application/json", Content::new(C::to_schema(components))),
)
}
}
impl<C> ToResponse for Json<C>
where
C: ToSchema,
{
#[inline]
fn to_response(components: &mut Components) -> RefOr<Response> {
let schema = <C as ToSchema>::to_schema(components);
Response::new("Response with json format data")
.add_content("application/json", Content::new(schema))
.into()
}
}
impl<'de> HandlerArg for FilePart {
fn register(_: &mut Components, operation: &mut Operation, _arg: &str) {
let schema = Schema::from(
Object::new().property(
_arg,
Object::with_type(SchemaType::String)
.format(SchemaFormat::KnownFormat(KnownFormat::Binary)),
),
);
if let Some(request_body) = &mut operation.request_body {
request_body
.content
.insert("multipart/form-data".into(), Content::new(schema));
} else {
let request_body = RequestBody::new()
.description("Upload a file.")
.add_content("multipart/form-data", Content::new(schema));
operation.request_body = Some(request_body);
}
}
}
impl<'de> HandlerArg for FileParts {
fn register(_: &mut Components, operation: &mut Operation, _arg: &str) {
let schema = Schema::from(
Object::new().property(
_arg,
Array::new(Schema::from(
Object::with_type(SchemaType::String)
.format(SchemaFormat::KnownFormat(KnownFormat::Binary)),
)),
),
);
if let Some(request_body) = &mut operation.request_body {
request_body
.content
.insert("multipart/form-data".into(), Content::new(schema));
} else {
let request_body = RequestBody::new()
.description("Upload files.")
.add_content("multipart/form-data", Content::new(schema));
operation.request_body = Some(request_body);
}
}
}
impl<T> HandlerArg for CookieParam<T>
where
T: ToSchema,
{
fn register(components: &mut Components, operation: &mut Operation, arg: &str) {
let parameter = Parameter::new(arg)
.parameter_in(ParameterIn::Cookie)
.description(format!("Get parameter `{arg}` from request cookie."))
.schema(T::to_schema(components))
.required(true);
operation.parameters.insert(parameter);
}
}
impl<T> HandlerArg for hypers_core::prelude::Header<T>
where
T: ToSchema,
{
fn register(components: &mut Components, operation: &mut Operation, arg: &str) {
let parameter = Parameter::new(arg)
.parameter_in(ParameterIn::Header)
.description(format!("Get parameter `{arg}` from request headers."))
.schema(T::to_schema(components))
.required(true);
operation.parameters.insert(parameter);
}
}
impl<T> HandlerArg for Path<T>
where
T: ToSchema,
{
fn register(components: &mut Components, operation: &mut Operation, arg: &str) {
let parameter = Parameter::new(arg)
.parameter_in(ParameterIn::Path)
.description(format!("Get parameter `{arg}` from request url path."))
.schema(T::to_schema(components))
.required(true);
operation.parameters.insert(parameter);
}
}
impl<T> HandlerArg for Query<T>
where
T: ToSchema,
{
fn register(components: &mut Components, operation: &mut Operation, arg: &str) {
let parameter = Parameter::new(arg)
.parameter_in(ParameterIn::Query)
.description(format!("Get parameter `{arg}` from request url query."))
.schema(T::to_schema(components))
.required(true);
operation.parameters.insert(parameter);
}
}
impl<'de, T> ToRequestBody for Json<T>
where
T: Deserialize<'de> + ToSchema,
{
fn to_request_body(components: &mut Components) -> RequestBody {
RequestBody::new()
.description("Extract json format data from request.")
.add_content("application/json", Content::new(T::to_schema(components)))
}
}
impl<'de, T> HandlerArg for Json<T>
where
T: Deserialize<'de> + ToSchema,
{
fn register(components: &mut Components, operation: &mut Operation, _arg: &str) {
let request_body = Self::to_request_body(components);
let _ = <T as ToSchema>::to_schema(components);
operation.request_body = Some(request_body);
}
}
impl<'de, T> ToRequestBody for Form<T>
where
T: Deserialize<'de> + ToSchema,
{
fn to_request_body(components: &mut Components) -> RequestBody {
RequestBody::new()
.description("Extract form format data from request.")
.add_content(
"application/x-www-form-urlencoded",
Content::new(T::to_schema(components)),
)
.add_content("multipart/*", Content::new(T::to_schema(components)))
}
}
impl<'de, T> HandlerArg for Form<T>
where
T: Deserialize<'de> + ToSchema,
{
fn register(components: &mut Components, operation: &mut Operation, _arg: &str) {
let request_body = Self::to_request_body(components);
let _ = <T as ToSchema>::to_schema(components);
operation.request_body = Some(request_body);
}
}