use crate::tina::{data::app_error::AppError, util::schema::SchemaExt};
use crate::tina::{
data::http::{
request_json::HttpReqJson, request_metadata::HttpReqMetadata, request_multipart::HttpReqMultipart, request_param::HttpReqParam,
request_path::HttpReqPath,
},
server::http::route::{HttpMethod, RouteBaseConfig, RouteBuilder},
};
use std::any::type_name;
use crate::app_system_error;
use crate::tina::data::api_schema::ApiSchema;
use crate::tina::data::http::response_data::HttpResData;
use crate::tina::data::http::response_page::HttpResPage;
use crate::tina::data::http::response_stream::ResStream;
use crate::tina::data::json::ToJson;
use crate::tina::data::validate::Validated;
use crate::tina::data::AppResult;
use crate::tina::server::application::Application;
use crate::tina::server::http::axum::delegate::RouteHandlerDelegate;
use crate::tina::server::http::response::{ResRaw, ResponseAttribute};
use crate::tina::server::session::Session;
use crate::tina::util::json::JsonUtil;
use axum::extract::{FromRequest, FromRequestParts};
use axum::handler::Handler;
use axum::response::IntoResponse;
use axum::routing::{on, MethodFilter, MethodRouter};
use hyper::Body;
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json::Value;
use std::fmt::Debug;
use std::future::Future;
use std::pin::Pin;
use utoipa::openapi::path::Parameter;
use utoipa::{
openapi::{
path::{ParameterBuilder, ParameterIn},
request_body::{RequestBody, RequestBodyBuilder},
ContentBuilder, RefOr, Required, Responses,
},
ToSchema,
};
use super::request_conversion::ViaCustom;
pub trait ToMethodFilter {
fn to_method_filter(&self) -> MethodFilter;
}
impl ToMethodFilter for HttpMethod {
fn to_method_filter(&self) -> MethodFilter {
match self {
HttpMethod::OPTIONS => MethodFilter::OPTIONS,
HttpMethod::GET => MethodFilter::GET,
HttpMethod::POST => MethodFilter::POST,
HttpMethod::PUT => MethodFilter::PUT,
HttpMethod::DELETE => MethodFilter::DELETE,
HttpMethod::HEAD => MethodFilter::HEAD,
HttpMethod::TRACE => MethodFilter::TRACE,
HttpMethod::CONNECT => panic!("unsupoorted method: connect"),
HttpMethod::PATCH => MethodFilter::PATCH,
}
}
}
pub struct RouteConfig<S = (), B = Body> {
pub(crate) route: Option<MethodRouter<S, B>>,
pub(crate) config: RouteBaseConfig,
}
impl RouteConfig {
fn basic<S, B>(self) -> RouteConfig<S, B> {
RouteConfig {
route: None,
config: self.config,
}
}
}
impl RouteBuilder {
pub fn to<F, Args, T, S, B, M>(&mut self, handler: F) -> RouteConfig<S, B>
where
F: ApiHandler<Args, S, B, M> + Handler<T, S, B> + Clone + 'static,
Args: FromApiRequest<S, B, M> + FromRequest<S, B, M> + Send + Sync + 'static,
F::Output1: ApiResponder<AppError> + Send + Sync + 'static,
T: 'static,
S: Clone + Send + Sync + 'static,
B: http_body::Body + Debug + Send + Sync + 'static,
M: Send + 'static,
{
self.request_parameter = Args::get_request_parameter();
self.request_body = Args::get_request_body();
self.response_body = F::Output1::get_response_body();
self.handler_name = type_name::<F>().to_string();
let mut route_config = self.to_route().basic();
let route = match route_config.config.should_delegate_handler() {
true => {
let handler: RouteHandlerDelegate<F, Args, S, B, M, AppError> = RouteHandlerDelegate::new(handler);
on(route_config.config.method.to_method_filter(), handler)
}
false => on(route_config.config.method.to_method_filter(), handler),
};
route_config.route = Some(route);
route_config
}
}
pub trait FromApiRequest<S, B, M> {
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized;
fn get_request_body() -> Option<RequestBody>
where
Self: Sized;
fn to_param_value(&self) -> AppResult<Value>;
}
pub trait ApiResponder<Err>: IntoResponse {
fn get_response_body() -> Option<Responses>
where
Self: Sized;
fn is_success(&self) -> bool;
}
pub trait ApiHandler<T, S, B, M> {
type Future1: Future<Output = Self::Output1> + Send + 'static;
type Output1: ApiResponder<AppError>;
fn call(self, args: T, state: S) -> Self::Future1;
}
impl<S, B, M> FromApiRequest<S, B, M> for ViaCustom {
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
None
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
None
}
fn to_param_value(&self) -> AppResult<Value> {
Ok(Value::Null)
}
}
impl<'a, D: DeserializeOwned + Serialize + Validated + ToSchema<'a>, S, B, M> FromApiRequest<S, B, M> for HttpReqJson<D> {
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
None
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
let (_, schema) = D::schema();
Some(RequestBodyBuilder::new().content("application/json", ContentBuilder::new().schema(schema).build()).build())
}
fn to_param_value(&self) -> AppResult<Value> {
Ok(self.to_json_value())
}
}
impl<'a, D: DeserializeOwned + Serialize + Validated + ToSchema<'a>, S, B, M> FromApiRequest<S, B, M> for HttpReqPath<D> {
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
let schema = match D::schema().1 {
RefOr::Ref(_) => panic!("schema cannot be ref for 'get_request_parameter'"),
RefOr::T(v) => v,
};
let requires = schema.get_required();
let props = schema.get_schema_props();
let mut params = Vec::<Parameter>::with_capacity(props.len());
for (name, prop) in props.into_iter() {
let required = match requires.contains(&name) {
true => Required::True,
false => Required::False,
};
let param = ParameterBuilder::new()
.description(Some(prop.get_description()))
.required(required)
.parameter_in(ParameterIn::Path)
.schema(Some(prop))
.build();
params.push(param);
}
Some(params)
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
None
}
fn to_param_value(&self) -> AppResult<Value> {
let value = self.to_json_value();
match value {
Value::Object(v) => Ok(Value::Object(v)),
_ => {
let name = self
.names()
.iter()
.next()
.map(|v| v.to_string())
.ok_or_else(|| app_system_error!("no param name found in ReqPath: {}", type_name::<Self>()))?;
let mut map = serde_json::Map::new();
map.insert(name, value);
Ok(Value::Object(map))
}
}
}
}
impl<'a, D: DeserializeOwned + Serialize + Validated + ToSchema<'a>, S, B, M> FromApiRequest<S, B, M> for HttpReqParam<D> {
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
let schema = match D::schema().1 {
RefOr::Ref(_) => panic!("schema cannot be ref for 'get_request_parameter'"),
RefOr::T(v) => v,
};
let requires = schema.get_required();
let props = schema.get_schema_props();
let mut params = Vec::<Parameter>::with_capacity(props.len());
for (name, prop) in props.into_iter() {
let required = match requires.contains(&name) {
true => Required::True,
false => Required::False,
};
let param = ParameterBuilder::new()
.description(Some(prop.get_description()))
.required(required)
.parameter_in(ParameterIn::Query)
.schema(Some(prop))
.build();
params.push(param);
}
Some(params)
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
let (_, schema) = D::schema();
Some(RequestBodyBuilder::new().content("application/x-www-form-urlencoded", ContentBuilder::new().schema(schema).build()).build())
}
fn to_param_value(&self) -> AppResult<Value> {
Ok(self.to_json_value())
}
}
impl<'a, D: DeserializeOwned + Serialize + Validated + ToSchema<'a>, S, B, M> FromApiRequest<S, B, M> for HttpReqMultipart<D> {
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
None
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
let (_, schema) = D::schema();
Some(RequestBodyBuilder::new().content("multipart/form-data", ContentBuilder::new().schema(schema).build()).build())
}
fn to_param_value(&self) -> AppResult<Value> {
Ok(self.to_json_value())
}
}
impl<S, B, M> FromApiRequest<S, B, M> for Session {
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
None
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
None
}
fn to_param_value(&self) -> AppResult<Value> {
Ok(Value::Null)
}
}
impl<S, B, M> FromApiRequest<S, B, M> for Application {
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
None
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
None
}
fn to_param_value(&self) -> AppResult<Value> {
Ok(Value::Null)
}
}
impl<S, B, M> FromApiRequest<S, B, M> for HttpReqMetadata {
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
None
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
None
}
fn to_param_value(&self) -> AppResult<Value> {
Ok(Value::Null)
}
}
impl<S, B, M> FromApiRequest<S, B, M> for () {
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
None
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
None
}
fn to_param_value(&self) -> AppResult<Value> {
Ok(Value::Null)
}
}
impl<T1, S, B, M> FromApiRequest<S, B, M> for (T1,)
where
T1: FromApiRequest<S, B, M> + FromRequest<S, B, M> + 'static,
{
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
let mut params = Vec::new();
if let Some(mut v) = T1::get_request_parameter() {
params.append(&mut v);
}
match params.is_empty() {
true => None,
false => Some(params),
}
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
if let Some(v) = T1::get_request_body() {
return Some(v);
}
None
}
fn to_param_value(&self) -> AppResult<Value> {
self.0.to_param_value()
}
}
impl<T1, T2, S, B, M> FromApiRequest<S, B, M> for (T1, T2)
where
T1: FromApiRequest<S, B, M> + FromRequestParts<S> + 'static,
T2: FromApiRequest<S, B, M> + FromRequest<S, B, M> + 'static,
{
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
let mut params = Vec::new();
if let Some(mut v) = T1::get_request_parameter() {
params.append(&mut v);
}
if let Some(mut v) = T2::get_request_parameter() {
params.append(&mut v);
}
match params.is_empty() {
true => None,
false => Some(params),
}
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
if let Some(v) = T1::get_request_body() {
return Some(v);
}
if let Some(v) = T2::get_request_body() {
return Some(v);
}
None
}
fn to_param_value(&self) -> AppResult<Value> {
let v1 = JsonUtil::merge_value(self.0.to_param_value()?, self.1.to_param_value()?)?;
Ok(v1)
}
}
impl<T1, T2, T3, S, B, M> FromApiRequest<S, B, M> for (T1, T2, T3)
where
T1: FromApiRequest<S, B, M> + FromRequestParts<S> + 'static,
T2: FromApiRequest<S, B, M> + FromRequestParts<S> + 'static,
T3: FromApiRequest<S, B, M> + FromRequest<S, B, M> + 'static,
{
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
let mut params = Vec::new();
if let Some(mut v) = T1::get_request_parameter() {
params.append(&mut v);
}
if let Some(mut v) = T2::get_request_parameter() {
params.append(&mut v);
}
if let Some(mut v) = T3::get_request_parameter() {
params.append(&mut v);
}
match params.is_empty() {
true => None,
false => Some(params),
}
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
if let Some(v) = T1::get_request_body() {
return Some(v);
}
if let Some(v) = T2::get_request_body() {
return Some(v);
}
if let Some(v) = T3::get_request_body() {
return Some(v);
}
None
}
fn to_param_value(&self) -> AppResult<Value> {
let v1 = JsonUtil::merge_value(self.0.to_param_value()?, self.1.to_param_value()?)?;
let v2 = JsonUtil::merge_value(v1, self.2.to_param_value()?)?;
Ok(v2)
}
}
impl<T1, T2, T3, T4, S, B, M> FromApiRequest<S, B, M> for (T1, T2, T3, T4)
where
T1: FromApiRequest<S, B, M> + FromRequestParts<S> + 'static,
T2: FromApiRequest<S, B, M> + FromRequestParts<S> + 'static,
T3: FromApiRequest<S, B, M> + FromRequestParts<S> + 'static,
T4: FromApiRequest<S, B, M> + FromRequest<S, B, M> + 'static,
{
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
let mut params = Vec::new();
if let Some(mut v) = T1::get_request_parameter() {
params.append(&mut v);
}
if let Some(mut v) = T2::get_request_parameter() {
params.append(&mut v);
}
if let Some(mut v) = T3::get_request_parameter() {
params.append(&mut v);
}
if let Some(mut v) = T4::get_request_parameter() {
params.append(&mut v);
}
match params.is_empty() {
true => None,
false => Some(params),
}
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
if let Some(v) = T1::get_request_body() {
return Some(v);
}
if let Some(v) = T2::get_request_body() {
return Some(v);
}
if let Some(v) = T3::get_request_body() {
return Some(v);
}
if let Some(v) = T4::get_request_body() {
return Some(v);
}
None
}
fn to_param_value(&self) -> AppResult<Value> {
let v1 = JsonUtil::merge_value(self.0.to_param_value()?, self.1.to_param_value()?)?;
let v2 = JsonUtil::merge_value(v1, self.2.to_param_value()?)?;
let v3 = JsonUtil::merge_value(v2, self.3.to_param_value()?)?;
Ok(v3)
}
}
impl<T1, T2, T3, T4, T5, S, B, M> FromApiRequest<S, B, M> for (T1, T2, T3, T4, T5)
where
T1: FromApiRequest<S, B, M> + FromRequestParts<S> + 'static,
T2: FromApiRequest<S, B, M> + FromRequestParts<S> + 'static,
T3: FromApiRequest<S, B, M> + FromRequestParts<S> + 'static,
T4: FromApiRequest<S, B, M> + FromRequestParts<S> + 'static,
T5: FromApiRequest<S, B, M> + FromRequest<S, B, M> + 'static,
{
fn get_request_parameter() -> Option<Vec<Parameter>>
where
Self: Sized,
{
let mut params = Vec::new();
if let Some(mut v) = T1::get_request_parameter() {
params.append(&mut v);
}
if let Some(mut v) = T2::get_request_parameter() {
params.append(&mut v);
}
if let Some(mut v) = T3::get_request_parameter() {
params.append(&mut v);
}
if let Some(mut v) = T4::get_request_parameter() {
params.append(&mut v);
}
if let Some(mut v) = T5::get_request_parameter() {
params.append(&mut v);
}
match params.is_empty() {
true => None,
false => Some(params),
}
}
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
if let Some(v) = T1::get_request_body() {
return Some(v);
}
if let Some(v) = T2::get_request_body() {
return Some(v);
}
if let Some(v) = T3::get_request_body() {
return Some(v);
}
if let Some(v) = T4::get_request_body() {
return Some(v);
}
if let Some(v) = T5::get_request_body() {
return Some(v);
}
None
}
fn to_param_value(&self) -> AppResult<Value> {
let v1 = JsonUtil::merge_value(self.0.to_param_value()?, self.1.to_param_value()?)?;
let v2 = JsonUtil::merge_value(v1, self.2.to_param_value()?)?;
let v3 = JsonUtil::merge_value(v2, self.3.to_param_value()?)?;
let v4 = JsonUtil::merge_value(v3, self.4.to_param_value()?)?;
Ok(v4)
}
}
impl<D: ApiResponder<Err>, Err> ApiResponder<Err> for AppResult<D> {
fn get_response_body() -> Option<Responses>
where
Self: Sized,
{
D::get_response_body()
}
fn is_success(&self) -> bool {
match &self {
Ok(v) => v.is_success(),
Err(_) => false,
}
}
}
impl<'a, D: Serialize + Debug + ToSchema<'a>, Ext: Serialize + Debug + ToSchema<'a>, Err> ApiResponder<Err> for HttpResData<D, Ext> {
fn get_response_body() -> Option<Responses>
where
Self: Sized,
{
Some(<HttpResData<D, Ext> as ApiSchema>::get_responses())
}
fn is_success(&self) -> bool {
self.code.is_success()
}
}
impl<'a, D: Serialize + Debug + ToSchema<'a>, Err> ApiResponder<Err> for HttpResPage<D> {
fn get_response_body() -> Option<Responses>
where
Self: Sized,
{
Some(<HttpResPage<D> as ApiSchema>::get_responses())
}
fn is_success(&self) -> bool {
self.code.is_success()
}
}
impl<Err> ApiResponder<Err> for ResStream {
fn get_response_body() -> Option<Responses>
where
Self: Sized,
{
Some(<ResStream as ApiSchema>::get_responses())
}
fn is_success(&self) -> bool {
self.success()
}
}
impl<Err> ApiResponder<Err> for ResRaw {
fn get_response_body() -> Option<Responses>
where
Self: Sized,
{
None
}
fn is_success(&self) -> bool {
self.success()
}
}
macro_rules! impl_handler {
(
[$($ty:ident, $arg:ident),*], $last:ident, $last_arg:ident
) => {
#[allow(non_snake_case, unused)]
impl<F, Fut, S, B, Res, M, $($ty,)* $last> ApiHandler<($($ty,)* $last,), S, B, M> for F
where
F: Handler<(M, $($ty,)* $last,), S, B> + FnOnce($($ty,)* $last,) -> Fut + Clone + Send + 'static,
Fut: Future<Output = Res> + Send,
B: http_body::Body + Send + 'static,
S: Clone + Send + Sync + 'static,
Res: ApiResponder<AppError> + Send + Sync + 'static,
$( $ty: FromApiRequest<S, B, M> + FromRequestParts<S> + Send + Sync + 'static, )*
$last: FromApiRequest<S, B, M> + FromRequest<S, B, M> + Send + Sync + 'static,
{
type Future1 = Pin<Box<dyn Future<Output = Res> + Send>>;
type Output1 = Res;
fn call(self, ($($arg,)* $last_arg,): ($($ty,)* $last,), state: S) -> Self::Future1 {
Box::pin(async move {
let res = self($($arg,)* $last_arg).await;
res
}) as Pin<Box<dyn Future<Output = Res> + Send>>
}
}
};
}
#[rustfmt::skip]
macro_rules! all_the_tuples {
($name:ident) => {
$name!([], T1, arg1);
$name!([T1, arg1], T2, arg2);
$name!([T1, arg1, T2, arg2], T3, arg3);
$name!([T1, arg1, T2, arg2, T3, arg3], T4, arg4);
$name!([T1, arg1, T2, arg2, T3, arg3, T4, arg4], T5, arg5);
$name!([T1, arg1, T2, arg2, T3, arg3, T4, arg4, T5, arg5], T6, arg6);
$name!([T1, arg1, T2, arg2, T3, arg3, T4, arg4, T5, arg5, T6, arg6], T7, arg7);
$name!([T1, arg1, T2, arg2, T3, arg3, T4, arg4, T5, arg5, T6, arg6, T7, arg7], T8, arg8);
$name!([T1, arg1, T2, arg2, T3, arg3, T4, arg4, T5, arg5, T6, arg6, T7, arg7, T8, arg8], T9, arg9);
$name!([T1, arg1, T2, arg2, T3, arg3, T4, arg4, T5, arg5, T6, arg6, T7, arg7, T8, arg8, T9, arg9], T10, arg10);
$name!([T1, arg1, T2, arg2, T3, arg3, T4, arg4, T5, arg5, T6, arg6, T7, arg7, T8, arg8, T9, arg9, T10, arg10], T11, arg11);
$name!([T1, arg1, T2, arg2, T3, arg3, T4, arg4, T5, arg5, T6, arg6, T7, arg7, T8, arg8, T9, arg9, T10, arg10, T11, arg11], T12, arg12);
$name!([T1, arg1, T2, arg2, T3, arg3, T4, arg4, T5, arg5, T6, arg6, T7, arg7, T8, arg8, T9, arg9, T10, arg10, T11, arg11, T12, arg12], T13, arg13);
$name!([T1, arg1, T2, arg2, T3, arg3, T4, arg4, T5, arg5, T6, arg6, T7, arg7, T8, arg8, T9, arg9, T10, arg10, T11, arg11, T12, arg12, T13, arg13], T14, arg14);
$name!([T1, arg1, T2, arg2, T3, arg3, T4, arg4, T5, arg5, T6, arg6, T7, arg7, T8, arg8, T9, arg9, T10, arg10, T11, arg11, T12, arg12, T13, arg13, T14, arg14], T15, arg15);
$name!([T1, arg1, T2, arg2, T3, arg3, T4, arg4, T5, arg5, T6, arg6, T7, arg7, T8, arg8, T9, arg9, T10, arg10, T11, arg11, T12, arg12, T13, arg13, T14, arg14, T15, arg15], T16, arg16);
};
}
all_the_tuples!(impl_handler);