use crate::api_description::ApiEndpointParameter;
use crate::api_description::{ApiEndpointBodyContentType, ExtensionMode};
use crate::error::HttpError;
use crate::server::ServerContext;
use crate::RequestContext;
use async_trait::async_trait;
pub struct ExtractorMetadata {
pub extension_mode: ExtensionMode,
pub parameters: Vec<ApiEndpointParameter>,
}
#[async_trait]
pub trait ExclusiveExtractor: Send + Sync + Sized {
async fn from_request<Context: ServerContext>(
rqctx: &RequestContext<Context>,
request: hyper::Request<crate::Body>,
) -> Result<Self, HttpError>;
fn metadata(
body_content_type: ApiEndpointBodyContentType,
) -> ExtractorMetadata;
}
#[async_trait]
pub trait SharedExtractor: Send + Sync + Sized {
async fn from_request<Context: ServerContext>(
rqctx: &RequestContext<Context>,
) -> Result<Self, HttpError>;
fn metadata(
body_content_type: ApiEndpointBodyContentType,
) -> ExtractorMetadata;
}
#[async_trait]
impl<S: SharedExtractor> ExclusiveExtractor for S {
async fn from_request<Context: ServerContext>(
rqctx: &RequestContext<Context>,
_request: hyper::Request<crate::Body>,
) -> Result<Self, HttpError> {
<S as SharedExtractor>::from_request(rqctx).await
}
fn metadata(
body_content_type: ApiEndpointBodyContentType,
) -> ExtractorMetadata {
<S as SharedExtractor>::metadata(body_content_type)
}
}
#[async_trait]
pub trait RequestExtractor: Send + Sync + Sized {
async fn from_request<Context: ServerContext>(
rqctx: &RequestContext<Context>,
request: hyper::Request<crate::Body>,
) -> Result<Self, HttpError>;
fn metadata(
body_content_type: ApiEndpointBodyContentType,
) -> ExtractorMetadata;
}
#[async_trait]
impl RequestExtractor for () {
async fn from_request<Context: ServerContext>(
_rqctx: &RequestContext<Context>,
_request: hyper::Request<crate::Body>,
) -> Result<Self, HttpError> {
Ok(())
}
fn metadata(
_body_content_type: ApiEndpointBodyContentType,
) -> ExtractorMetadata {
ExtractorMetadata {
extension_mode: ExtensionMode::None,
parameters: vec![],
}
}
}
#[async_trait]
impl<X: ExclusiveExtractor + 'static> RequestExtractor for (X,) {
async fn from_request<Context: ServerContext>(
rqctx: &RequestContext<Context>,
request: hyper::Request<crate::Body>,
) -> Result<Self, HttpError> {
Ok((X::from_request(rqctx, request).await?,))
}
fn metadata(
body_content_type: ApiEndpointBodyContentType,
) -> ExtractorMetadata {
X::metadata(body_content_type)
}
}
macro_rules! impl_rqextractor_for_tuple {
($( $S:ident),+) => {
#[async_trait]
impl< X: ExclusiveExtractor + 'static, $($S: SharedExtractor + 'static,)+ >
RequestExtractor
for ($($S,)+ X)
{
async fn from_request<Context: ServerContext>(
rqctx: &RequestContext<Context>,
request: hyper::Request<crate::Body>
) -> Result<( $($S,)+ X ), HttpError>
{
futures::try_join!(
$($S::from_request(rqctx),)+
X::from_request(rqctx, request)
)
}
fn metadata(_body_content_type: ApiEndpointBodyContentType) -> ExtractorMetadata {
#[allow(unused_mut)]
let mut extension_mode = ExtensionMode::None;
#[allow(unused_mut)]
let mut parameters = vec![];
$(
let mut metadata = $S::metadata(_body_content_type.clone());
extension_mode = match (extension_mode, metadata.extension_mode) {
(ExtensionMode::None, x) | (x, ExtensionMode::None) => x,
(x, y) if x != y => {
panic!("incompatible extension modes in tuple: {:?} != {:?}", x, y);
}
(_, x) => x,
};
parameters.append(&mut metadata.parameters);
)+
let mut metadata = X::metadata(_body_content_type.clone());
extension_mode = match (extension_mode, metadata.extension_mode) {
(ExtensionMode::None, x) | (x, ExtensionMode::None) => x,
(x, y) if x != y => {
panic!("incompatible extension modes in tuple: {:?} != {:?}", x, y);
}
(_, x) => x,
};
parameters.append(&mut metadata.parameters);
ExtractorMetadata { extension_mode, parameters }
}
}
}}
impl_rqextractor_for_tuple!(S1);
impl_rqextractor_for_tuple!(S1, S2);
impl_rqextractor_for_tuple!(S1, S2, S3);
impl_rqextractor_for_tuple!(S1, S2, S3, S4);