use std::convert::TryInto;
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::time::Duration;
#[doc(inline)]
pub use crate::cycle;
pub trait Responder: Send {
fn respond<'a>(
&mut self,
req: &'a http::Request<bytes::Bytes>,
) -> Pin<Box<dyn Future<Output = http::Response<hyper::Body>> + Send + 'a>>;
}
#[derive(Debug)]
pub struct ResponseBuilder<B>(http::Response<B>);
impl<B> ResponseBuilder<B> {
pub fn version(mut self, version: http::Version) -> Self {
*self.0.version_mut() = version;
self
}
pub fn insert_header<K, V>(mut self, name: K, value: V) -> Self
where
K: TryInto<http::header::HeaderName>,
K::Error: fmt::Debug,
V: TryInto<http::header::HeaderValue>,
V::Error: fmt::Debug,
{
let name: http::header::HeaderName = name.try_into().expect("invalid header name");
let value: http::header::HeaderValue = value.try_into().expect("invalid header value");
self.0.headers_mut().insert(name, value);
self
}
pub fn append_header<K, V>(mut self, name: K, value: V) -> Self
where
K: TryInto<http::header::HeaderName>,
K::Error: fmt::Debug,
V: TryInto<http::header::HeaderValue>,
V::Error: fmt::Debug,
{
let name: http::header::HeaderName = name.try_into().expect("invalid header name");
let value: http::header::HeaderValue = value.try_into().expect("invalid header value");
self.0.headers_mut().append(name, value);
self
}
pub fn body<B2>(self, body: B2) -> ResponseBuilder<B2> {
ResponseBuilder(self.0.map(|_| body))
}
}
pub fn status_code(code: u16) -> ResponseBuilder<&'static str> {
ResponseBuilder(
http::Response::builder()
.status(code)
.body("")
.expect("invalid status code"),
)
}
pub fn json_encoded<T>(data: T) -> ResponseBuilder<String>
where
T: serde::Serialize,
{
status_code(200)
.append_header("Content-Type", "application/json")
.body(serde_json::to_string(&data).expect("failed to serialize body"))
}
pub fn url_encoded<T>(data: T) -> ResponseBuilder<String>
where
T: serde::Serialize,
{
status_code(200)
.append_header("Content-Type", "application/x-www-form-urlencoded")
.body(serde_urlencoded::to_string(&data).expect("failed to serialize body"))
}
impl<B> Responder for ResponseBuilder<B>
where
B: Clone + Into<hyper::Body> + Send + fmt::Debug,
{
fn respond<'a>(
&mut self,
req: &'a http::Request<bytes::Bytes>,
) -> Pin<Box<dyn Future<Output = http::Response<hyper::Body>> + Send + 'a>> {
self.0.respond(req)
}
}
pub struct Delay<R: Responder> {
delay: Duration,
and_then: R,
}
pub fn delay_and_then<R: Responder>(delay: Duration, and_then: R) -> Delay<R> {
Delay { delay, and_then }
}
impl<R: Responder> Responder for Delay<R> {
fn respond<'a>(
&mut self,
req: &'a http::Request<bytes::Bytes>,
) -> Pin<Box<dyn Future<Output = http::Response<hyper::Body>> + Send + 'a>> {
let resp = self.and_then.respond(req);
let delay = self.delay;
Box::pin(async move {
tokio::time::sleep(delay).await;
resp.await
})
}
}
impl<B> Responder for http::Response<B>
where
B: Clone + Into<hyper::Body> + Send + fmt::Debug,
{
fn respond<'a>(
&mut self,
_req: &'a http::Request<bytes::Bytes>,
) -> Pin<Box<dyn Future<Output = http::Response<hyper::Body>> + Send + 'a>> {
async fn _respond(resp: http::Response<hyper::Body>) -> http::Response<hyper::Body> {
resp
}
let mut builder = http::Response::builder();
builder = builder.status(self.status()).version(self.version());
*builder.headers_mut().unwrap() = self.headers().clone();
let resp = builder.body(self.body().clone().into()).unwrap();
Box::pin(_respond(resp))
}
}
impl<F, B> Responder for F
where
F: FnMut() -> B + Clone + Send + 'static,
B: Responder,
{
fn respond<'a>(
&mut self,
req: &'a http::Request<bytes::Bytes>,
) -> Pin<Box<dyn Future<Output = http::Response<hyper::Body>> + Send + 'a>> {
let mut f = self.clone();
Box::pin(async move { tokio::task::block_in_place(|| f()).respond(req).await })
}
}
pub fn cycle(responders: Vec<Box<dyn Responder>>) -> impl Responder {
if responders.is_empty() {
panic!("empty vector provided to cycle");
}
Cycle { idx: 0, responders }
}
pub struct Cycle {
idx: usize,
responders: Vec<Box<dyn Responder>>,
}
impl Responder for Cycle {
fn respond<'a>(
&mut self,
req: &'a http::Request<bytes::Bytes>,
) -> Pin<Box<dyn Future<Output = http::Response<hyper::Body>> + Send + 'a>> {
let idx = self.idx;
self.idx = (self.idx + 1) % self.responders.len();
self.responders[idx].respond(req)
}
}
#[cfg(test)]
mod tests {}