use crate::hyper::{Request, Response, StatusCode};
use crate::{error::BoxError, response::ResponseFuture, IntoResponseFuture};
use std::{borrow::Cow, convert::Infallible, error::Error as StdError};
pub trait Returning: Send + Sync + Sealed {
fn returning(&self, req: Request<String>) -> ResponseFuture;
}
pub trait Sealed {}
macro_rules! returning {
($type:ty, $body:expr, $($lt:lifetime),+) => {
returning!($type, $body, |_| Ok::<_, ::std::convert::Infallible>(crate::hyper::StatusCode::OK), |_| Ok::<_, ::std::convert::Infallible>(crate::hyper::HeaderMap::new()), $($lt),+);
};
($type:ty, $body:expr) => {
returning!($type, $body, |_| Ok::<_, ::std::convert::Infallible>(crate::hyper::StatusCode::OK), |_| Ok::<_, ::std::convert::Infallible>(crate::hyper::HeaderMap::new()));
};
($type:ty, $body:expr, $status:expr, $($lt:lifetime),+) => {
returning!($type, $body, $status, |_| Ok::<_, ::std::convert::Infallible>(crate::hyper::HeaderMap::new()), $($lt),+);
};
($type:ty, $body:expr, $status:expr) => {
returning!($type, $body, $status, |_| Ok::<_, ::std::convert::Infallible>(crate::hyper::HeaderMap::new()));
};
($type:ty, $body:expr, $status:expr, $headers:expr, $($lt:lifetime),+) => {
impl<$($lt),+> Returning for $type {
#[allow(clippy::redundant_closure_call)]
fn returning(&self, _req: crate::hyper::Request<String>) -> ResponseFuture {
#[allow(clippy::ptr_arg)]
fn response<$($lt),+>(s: &$type) -> Result<Response<String>, BoxError> {
let mut res = crate::hyper::Response::builder();
for (k, v) in ($headers)(s)?.iter() {
res = res.header(k, v);
}
Ok(res
.status(($status)(s)?)
.body(($body)(s)?)?)
}
let res: Result<Response<String>, BoxError> = response(self);
Box::pin(async move {
res
})
}
}
impl<$($lt),+> Sealed for $type {}
};
($type:ty, $body:expr, $status:expr, $headers:expr) => {
impl Returning for $type {
#[allow(clippy::redundant_closure_call)]
fn returning(&self, _req: crate::hyper::Request<String>) -> ResponseFuture {
fn response(s: &$type) -> Result<Response<String>, BoxError> {
let mut res = crate::hyper::Response::builder();
for (k, v) in ($headers)(s)?.iter() {
res = res.header(k, v);
}
Ok(res
.status(($status)(s)?)
.body(($body)(s)?)?)
}
let res: Result<Response<String>, BoxError> = response(self);
Box::pin(async move {
res
})
}
}
impl Sealed for $type {}
};
}
returning!(&'a str, |v: &&str| { Ok::<_, Infallible>(v.to_string()) }, 'a);
returning!(String, |v: &String| { Ok::<_, Infallible>(v.to_string()) });
returning!(Cow<'a, str>, |v: &Cow<'a, str>| { Ok::<_, Infallible>(v.to_string()) }, 'a);
returning!(
StatusCode,
|_| Ok::<_, Infallible>(String::new()),
|v: &StatusCode| Ok::<_, Infallible>(*v)
);
returning!(u16, |_| Ok::<_, Infallible>(String::new()), |v: &u16| {
StatusCode::try_from(*v)
});
#[cfg(feature = "json")]
returning!(serde_json::Value, |v: &serde_json::Value| {
Ok::<_, Infallible>(v.to_string())
});
impl<S, B> Returning for (S, B)
where
(S, B): Send + Sync,
S: TryInto<StatusCode> + Clone,
S::Error: StdError + Send + Sync + 'static,
B: ToString + 'static,
{
fn returning(&self, _req: Request<String>) -> ResponseFuture {
let status = self.0.clone().try_into();
let body = self.1.to_string();
Box::pin(async { Ok(Response::builder().status(status?).body(body)?) })
}
}
impl<S, B> Sealed for (S, B)
where
S: TryInto<StatusCode> + Clone,
S::Error: StdError + Send + Sync + 'static,
B: ToString,
{
}
impl<F, R> Returning for F
where
F: Fn(Request<String>) -> R + Send + Sync,
R: IntoResponseFuture,
{
fn returning(&self, req: Request<String>) -> ResponseFuture {
(self)(req).into_response_future()
}
}
impl<F, R> Sealed for F
where
F: Fn(Request<String>) -> R,
R: IntoResponseFuture,
{
}