132 lines
4.2 KiB
Rust
132 lines
4.2 KiB
Rust
//! Responder implementations.
|
|
//!
|
|
//! Reponders determine how the server will respond.
|
|
|
|
use std::fmt;
|
|
use std::future::Future;
|
|
use std::pin::Pin;
|
|
|
|
// import the cycle macro so that it's available if people glob import this module.
|
|
#[doc(inline)]
|
|
pub use crate::cycle;
|
|
|
|
/// Respond with an HTTP response.
|
|
pub trait Responder: Send + fmt::Debug {
|
|
/// Return a future that outputs an HTTP response.
|
|
fn respond(&mut self) -> Pin<Box<dyn Future<Output = hyper::Response<hyper::Body>> + Send>>;
|
|
}
|
|
|
|
/// respond with the provided status code.
|
|
pub fn status_code(code: u16) -> impl Responder {
|
|
StatusCode(code)
|
|
}
|
|
/// The `StatusCode` responder returned by [status_code()](fn.status_code.html)
|
|
#[derive(Debug)]
|
|
pub struct StatusCode(u16);
|
|
impl Responder for StatusCode {
|
|
fn respond(&mut self) -> Pin<Box<dyn Future<Output = hyper::Response<hyper::Body>> + Send>> {
|
|
async fn _respond(status_code: u16) -> hyper::Response<hyper::Body> {
|
|
hyper::Response::builder()
|
|
.status(status_code)
|
|
.body(hyper::Body::empty())
|
|
.unwrap()
|
|
}
|
|
Box::pin(_respond(self.0))
|
|
}
|
|
}
|
|
|
|
/// respond with a body that is the json encoding of data.
|
|
///
|
|
/// The status code will be `200` and the content-type will be
|
|
/// `application/json`.
|
|
pub fn json_encoded<T>(data: T) -> impl Responder
|
|
where
|
|
T: serde::Serialize,
|
|
{
|
|
JsonEncoded(serde_json::to_string(&data).unwrap())
|
|
}
|
|
/// The `JsonEncoded` responder returned by [json_encoded()](fn.json_encoded.html)
|
|
#[derive(Debug)]
|
|
pub struct JsonEncoded(String);
|
|
impl Responder for JsonEncoded {
|
|
fn respond(&mut self) -> Pin<Box<dyn Future<Output = hyper::Response<hyper::Body>> + Send>> {
|
|
async fn _respond(body: String) -> hyper::Response<hyper::Body> {
|
|
hyper::Response::builder()
|
|
.status(200)
|
|
.header("Content-Type", "application/json")
|
|
.body(body.into())
|
|
.unwrap()
|
|
}
|
|
Box::pin(_respond(self.0.clone()))
|
|
}
|
|
}
|
|
|
|
/// respond with a body that is the url encoding of data.
|
|
///
|
|
/// The status code will be `200` and the content-type will be
|
|
/// `application/x-www-form-urlencoded`.
|
|
pub fn url_encoded<T>(data: T) -> impl Responder
|
|
where
|
|
T: serde::Serialize,
|
|
{
|
|
UrlEncoded(serde_urlencoded::to_string(&data).unwrap())
|
|
}
|
|
/// The `UrlEncoded` responder returned by [url_encoded()](fn.url_encoded.html)
|
|
#[derive(Debug)]
|
|
pub struct UrlEncoded(String);
|
|
impl Responder for UrlEncoded {
|
|
fn respond(&mut self) -> Pin<Box<dyn Future<Output = hyper::Response<hyper::Body>> + Send>> {
|
|
async fn _respond(body: String) -> hyper::Response<hyper::Body> {
|
|
hyper::Response::builder()
|
|
.status(200)
|
|
.header("Content-Type", "application/x-www-form-urlencoded")
|
|
.body(body.into())
|
|
.unwrap()
|
|
}
|
|
Box::pin(_respond(self.0.clone()))
|
|
}
|
|
}
|
|
|
|
impl<B> Responder for hyper::Response<B>
|
|
where
|
|
B: Clone + Into<hyper::Body> + Send + fmt::Debug,
|
|
{
|
|
fn respond(&mut self) -> Pin<Box<dyn Future<Output = hyper::Response<hyper::Body>> + Send>> {
|
|
async fn _respond(resp: hyper::Response<hyper::Body>) -> hyper::Response<hyper::Body> {
|
|
resp
|
|
}
|
|
let mut builder = hyper::Response::builder();
|
|
builder = builder
|
|
.status(self.status().clone())
|
|
.version(self.version().clone());
|
|
*builder.headers_mut().unwrap() = self.headers().clone();
|
|
let resp = builder.body(self.body().clone().into()).unwrap();
|
|
|
|
Box::pin(_respond(resp))
|
|
}
|
|
}
|
|
|
|
/// Cycle through the provided list of responders.
|
|
pub fn cycle(responders: Vec<Box<dyn Responder>>) -> impl Responder {
|
|
if responders.is_empty() {
|
|
panic!("empty vector provided to cycle");
|
|
}
|
|
Cycle { idx: 0, responders }
|
|
}
|
|
/// The `Cycle` responder returned by [cycle()](fn.cycle.html)
|
|
#[derive(Debug)]
|
|
pub struct Cycle {
|
|
idx: usize,
|
|
responders: Vec<Box<dyn Responder>>,
|
|
}
|
|
impl Responder for Cycle {
|
|
fn respond(&mut self) -> Pin<Box<dyn Future<Output = hyper::Response<hyper::Body>> + Send>> {
|
|
let response = self.responders[self.idx].respond();
|
|
self.idx = (self.idx + 1) % self.responders.len();
|
|
response
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {}
|