use crate::extract::FromRequest;
use crate::request::Request;
use crate::response::{IntoResponse, Response};
use rustapi_openapi::{Operation, OperationModifier, ResponseModifier};
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
mod sealed {
pub trait Sealed {}
impl<T> Sealed for T where T: Clone + Send + Sync + Sized + 'static {}
}
pub trait Handler<T>: sealed::Sealed + Clone + Send + Sync + Sized + 'static {
type Future: Future<Output = Response> + Send + 'static;
fn call(self, req: Request) -> Self::Future;
fn update_operation(op: &mut Operation);
fn register_components(spec: &mut rustapi_openapi::OpenApiSpec);
}
pub struct HandlerService<H, T> {
handler: H,
_marker: PhantomData<fn() -> T>,
}
impl<H, T> HandlerService<H, T> {
pub fn new(handler: H) -> Self {
Self {
handler,
_marker: PhantomData,
}
}
}
impl<H: Clone, T> Clone for HandlerService<H, T> {
fn clone(&self) -> Self {
Self {
handler: self.handler.clone(),
_marker: PhantomData,
}
}
}
impl<F, Fut, Res> Handler<()> for F
where
F: FnOnce() -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Res> + Send + 'static,
Res: IntoResponse + ResponseModifier,
{
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
fn call(self, _req: Request) -> Self::Future {
Box::pin(async move { self().await.into_response() })
}
fn update_operation(op: &mut Operation) {
Res::update_response(op);
}
fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
Res::register_components(spec);
}
}
impl<F, Fut, Res, T1> Handler<(T1,)> for F
where
F: FnOnce(T1) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Res> + Send + 'static,
Res: IntoResponse + ResponseModifier,
T1: FromRequest + OperationModifier + Send + 'static,
{
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
fn call(self, mut req: Request) -> Self::Future {
Box::pin(async move {
let t1 = match T1::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
self(t1).await.into_response()
})
}
fn update_operation(op: &mut Operation) {
T1::update_operation(op);
Res::update_response(op);
}
fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
T1::register_components(spec);
Res::register_components(spec);
}
}
impl<F, Fut, Res, T1, T2> Handler<(T1, T2)> for F
where
F: FnOnce(T1, T2) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Res> + Send + 'static,
Res: IntoResponse + ResponseModifier,
T1: FromRequest + OperationModifier + Send + 'static,
T2: FromRequest + OperationModifier + Send + 'static,
{
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
fn call(self, mut req: Request) -> Self::Future {
Box::pin(async move {
let t1 = match T1::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
let t2 = match T2::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
self(t1, t2).await.into_response()
})
}
fn update_operation(op: &mut Operation) {
T1::update_operation(op);
T2::update_operation(op);
Res::update_response(op);
}
fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
T1::register_components(spec);
T2::register_components(spec);
Res::register_components(spec);
}
}
impl<F, Fut, Res, T1, T2, T3> Handler<(T1, T2, T3)> for F
where
F: FnOnce(T1, T2, T3) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Res> + Send + 'static,
Res: IntoResponse + ResponseModifier,
T1: FromRequest + OperationModifier + Send + 'static,
T2: FromRequest + OperationModifier + Send + 'static,
T3: FromRequest + OperationModifier + Send + 'static,
{
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
fn call(self, mut req: Request) -> Self::Future {
Box::pin(async move {
let t1 = match T1::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
let t2 = match T2::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
let t3 = match T3::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
self(t1, t2, t3).await.into_response()
})
}
fn update_operation(op: &mut Operation) {
T1::update_operation(op);
T2::update_operation(op);
T3::update_operation(op);
Res::update_response(op);
}
fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
T1::register_components(spec);
T2::register_components(spec);
T3::register_components(spec);
Res::register_components(spec);
}
}
impl<F, Fut, Res, T1, T2, T3, T4> Handler<(T1, T2, T3, T4)> for F
where
F: FnOnce(T1, T2, T3, T4) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Res> + Send + 'static,
Res: IntoResponse + ResponseModifier,
T1: FromRequest + OperationModifier + Send + 'static,
T2: FromRequest + OperationModifier + Send + 'static,
T3: FromRequest + OperationModifier + Send + 'static,
T4: FromRequest + OperationModifier + Send + 'static,
{
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
fn call(self, mut req: Request) -> Self::Future {
Box::pin(async move {
let t1 = match T1::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
let t2 = match T2::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
let t3 = match T3::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
let t4 = match T4::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
self(t1, t2, t3, t4).await.into_response()
})
}
fn update_operation(op: &mut Operation) {
T1::update_operation(op);
T2::update_operation(op);
T3::update_operation(op);
T4::update_operation(op);
Res::update_response(op);
}
fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
T1::register_components(spec);
T2::register_components(spec);
T3::register_components(spec);
T4::register_components(spec);
Res::register_components(spec);
}
}
impl<F, Fut, Res, T1, T2, T3, T4, T5> Handler<(T1, T2, T3, T4, T5)> for F
where
F: FnOnce(T1, T2, T3, T4, T5) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Res> + Send + 'static,
Res: IntoResponse + ResponseModifier,
T1: FromRequest + OperationModifier + Send + 'static,
T2: FromRequest + OperationModifier + Send + 'static,
T3: FromRequest + OperationModifier + Send + 'static,
T4: FromRequest + OperationModifier + Send + 'static,
T5: FromRequest + OperationModifier + Send + 'static,
{
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
fn call(self, mut req: Request) -> Self::Future {
Box::pin(async move {
let t1 = match T1::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
let t2 = match T2::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
let t3 = match T3::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
let t4 = match T4::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
let t5 = match T5::from_request(&mut req).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
self(t1, t2, t3, t4, t5).await.into_response()
})
}
fn update_operation(op: &mut Operation) {
T1::update_operation(op);
T2::update_operation(op);
T3::update_operation(op);
T4::update_operation(op);
T5::update_operation(op);
Res::update_response(op);
}
fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
T1::register_components(spec);
T2::register_components(spec);
T3::register_components(spec);
T4::register_components(spec);
T5::register_components(spec);
Res::register_components(spec);
}
}
pub(crate) type BoxedHandler =
std::sync::Arc<dyn Fn(Request) -> Pin<Box<dyn Future<Output = Response> + Send>> + Send + Sync>;
pub(crate) fn into_boxed_handler<H, T>(handler: H) -> BoxedHandler
where
H: Handler<T>,
T: 'static,
{
std::sync::Arc::new(move |req| {
let handler = handler.clone();
Box::pin(async move { handler.call(req).await })
})
}
pub trait RouteHandler<T>: Handler<T> {
const PATH: &'static str;
const METHOD: &'static str;
}
pub struct Route {
pub(crate) path: &'static str,
pub(crate) method: &'static str,
pub(crate) handler: BoxedHandler,
pub(crate) operation: Operation,
pub(crate) component_registrar: fn(&mut rustapi_openapi::OpenApiSpec),
pub(crate) param_schemas: std::collections::BTreeMap<String, String>,
pub(crate) error_responses: Vec<(u16, String)>,
}
impl Route {
pub fn new<H, T>(path: &'static str, method: &'static str, handler: H) -> Self
where
H: Handler<T>,
T: 'static,
{
let mut operation = Operation::new();
H::update_operation(&mut operation);
Self {
path,
method,
handler: into_boxed_handler(handler),
operation,
component_registrar: <H as Handler<T>>::register_components,
param_schemas: std::collections::BTreeMap::new(),
error_responses: Vec::new(),
}
}
pub fn summary(mut self, summary: impl Into<String>) -> Self {
self.operation = self.operation.summary(summary);
self
}
pub fn description(mut self, description: impl Into<String>) -> Self {
self.operation = self.operation.description(description);
self
}
pub fn tag(mut self, tag: impl Into<String>) -> Self {
self.operation.tags.push(tag.into());
self
}
pub fn path(&self) -> &str {
self.path
}
pub fn method(&self) -> &str {
self.method
}
pub fn param(mut self, name: impl Into<String>, schema_type: impl Into<String>) -> Self {
self.param_schemas.insert(name.into(), schema_type.into());
self
}
pub fn param_schemas(&self) -> &std::collections::BTreeMap<String, String> {
&self.param_schemas
}
pub fn error_response(mut self, status: u16, description: impl Into<String>) -> Self {
let desc = description.into();
self.error_responses.push((status, desc.clone()));
let mut content = std::collections::BTreeMap::new();
content.insert(
"application/json".to_string(),
rustapi_openapi::MediaType {
schema: Some(rustapi_openapi::SchemaRef::Ref {
reference: "#/components/schemas/ErrorSchema".to_string(),
}),
example: None,
},
);
self.operation.responses.insert(
status.to_string(),
rustapi_openapi::ResponseSpec {
description: desc,
content,
headers: std::collections::BTreeMap::new(),
},
);
self
}
pub fn error_responses(&self) -> &[(u16, String)] {
&self.error_responses
}
}
#[macro_export]
macro_rules! route {
($handler:ident) => {{
$crate::Route::new($handler::PATH, $handler::METHOD, $handler)
}};
}
pub fn get_route<H, T>(path: &'static str, handler: H) -> Route
where
H: Handler<T>,
T: 'static,
{
Route::new(path, "GET", handler)
}
pub fn post_route<H, T>(path: &'static str, handler: H) -> Route
where
H: Handler<T>,
T: 'static,
{
Route::new(path, "POST", handler)
}
pub fn put_route<H, T>(path: &'static str, handler: H) -> Route
where
H: Handler<T>,
T: 'static,
{
Route::new(path, "PUT", handler)
}
pub fn patch_route<H, T>(path: &'static str, handler: H) -> Route
where
H: Handler<T>,
T: 'static,
{
Route::new(path, "PATCH", handler)
}
pub fn delete_route<H, T>(path: &'static str, handler: H) -> Route
where
H: Handler<T>,
T: 'static,
{
Route::new(path, "DELETE", handler)
}