use std::error::Error as StdError;
use std::fmt;
use http::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use http::{HttpTryFrom, StatusCode};
use hyper::Body;
use serde::Serialize;
use serde_json;
use reject::Reject;
pub(crate) use self::sealed::{ReplyHttpError, ReplySealed, Reply_, Response};
#[doc(hidden)]
pub use filters::reply as with;
#[inline]
pub fn reply() -> impl Reply {
StatusCode::OK
}
pub fn json<T>(val: &T) -> impl Reply
where
T: Serialize,
{
Json {
inner: serde_json::to_vec(val).map_err(|err| {
error!("reply::json error: {}", err);
}),
}
}
#[allow(missing_debug_implementations)]
struct Json {
inner: Result<Vec<u8>, ()>,
}
impl ReplySealed for Json {
#[inline]
fn into_response(self) -> Response {
match self.inner {
Ok(body) => {
let mut res = Response::new(body.into());
res.headers_mut()
.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
res
}
Err(()) => ::reject::known(ReplyJsonError).into_response(),
}
}
}
#[derive(Debug)]
pub(crate) struct ReplyJsonError;
impl fmt::Display for ReplyJsonError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.description())
}
}
impl StdError for ReplyJsonError {
fn description(&self) -> &str {
"warp::reply::json() failed"
}
}
pub fn html<T>(body: T) -> impl Reply
where
Body: From<T>,
T: Send,
{
Html { body }
}
#[allow(missing_debug_implementations)]
struct Html<T> {
body: T,
}
impl<T> ReplySealed for Html<T>
where
Body: From<T>,
T: Send,
{
#[inline]
fn into_response(self) -> Response {
let mut res = Response::new(Body::from(self.body));
res.headers_mut().insert(
CONTENT_TYPE,
HeaderValue::from_static("text/html; charset=utf-8"),
);
res
}
}
pub trait Reply: ReplySealed {
}
impl<T: ReplySealed> Reply for T {}
fn _assert_object_safe() {
fn _assert(_: &Reply) {}
}
pub fn with_status<T: Reply>(reply: T, status: StatusCode) -> WithStatus<T> {
WithStatus { reply, status }
}
#[derive(Debug)]
pub struct WithStatus<T> {
reply: T,
status: StatusCode,
}
impl<T: Reply> ReplySealed for WithStatus<T> {
fn into_response(self) -> Response {
let mut res = self.reply.into_response();
*res.status_mut() = self.status;
res
}
}
pub fn with_header<T: Reply, K, V>(reply: T, name: K, value: V) -> WithHeader<T>
where
HeaderName: HttpTryFrom<K>,
HeaderValue: HttpTryFrom<V>,
{
let header = match <HeaderName as HttpTryFrom<K>>::try_from(name) {
Ok(name) => match <HeaderValue as HttpTryFrom<V>>::try_from(value) {
Ok(value) => Some((name, value)),
Err(err) => {
error!("with_header value error: {}", err.into());
None
}
},
Err(err) => {
error!("with_header name error: {}", err.into());
None
}
};
WithHeader { header, reply }
}
#[derive(Debug)]
pub struct WithHeader<T> {
header: Option<(HeaderName, HeaderValue)>,
reply: T,
}
impl<T: Reply> ReplySealed for WithHeader<T> {
fn into_response(self) -> Response {
let mut res = self.reply.into_response();
if let Some((name, value)) = self.header {
res.headers_mut().insert(name, value);
}
res
}
}
mod sealed {
use hyper::Body;
use generic::{Either, One};
use reject::Reject;
use super::Reply;
pub type Response = ::http::Response<Body>;
pub trait ReplySealed: Send {
fn into_response(self) -> Response;
}
pub fn __warp_replysealed_compilefail_doctest() {
let _ = ::reply().into_response();
}
#[allow(missing_debug_implementations)]
pub struct Reply_(pub(crate) Response);
impl ReplySealed for Reply_ {
#[inline]
fn into_response(self) -> Response {
self.0
}
}
impl<T: Send> ReplySealed for ::http::Response<T>
where
Body: From<T>,
{
#[inline]
fn into_response(self) -> Response {
self.map(Body::from)
}
}
impl ReplySealed for ::http::StatusCode {
#[inline]
fn into_response(self) -> Response {
let mut res = Response::default();
*res.status_mut() = self;
res
}
}
impl<T> ReplySealed for Result<T, ::http::Error>
where
T: Reply + Send,
{
#[inline]
fn into_response(self) -> Response {
match self {
Ok(t) => t.into_response(),
Err(e) => {
error!("reply error: {:?}", e);
::reject::known(ReplyHttpError(e)).into_response()
}
}
}
}
#[derive(Debug)]
pub(crate) struct ReplyHttpError(::http::Error);
impl ::std::fmt::Display for ReplyHttpError {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "http::Response::builder error: {}", self.0)
}
}
impl ::std::error::Error for ReplyHttpError {
fn description(&self) -> &str {
"http::Response::builder error"
}
}
impl ReplySealed for String {
#[inline]
fn into_response(self) -> Response {
Response::new(Body::from(self))
}
}
impl ReplySealed for &'static str {
#[inline]
fn into_response(self) -> Response {
Response::new(Body::from(self))
}
}
impl<T, U> ReplySealed for Either<T, U>
where
T: Reply,
U: Reply,
{
#[inline]
fn into_response(self) -> Response {
match self {
Either::A(a) => a.into_response(),
Either::B(b) => b.into_response(),
}
}
}
impl<T> ReplySealed for One<T>
where
T: Reply,
{
#[inline]
fn into_response(self) -> Response {
self.0.into_response()
}
}
impl ReplySealed for ::never::Never {
#[inline(always)]
fn into_response(self) -> Response {
match self {}
}
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::*;
#[test]
fn json_serde_error() {
let mut map = HashMap::new();
map.insert(vec![1, 2], 45);
let res = json(&map).into_response();
assert_eq!(res.status(), 500);
}
#[test]
fn response_builder_error() {
let res = ::http::Response::builder()
.status(1337)
.body("woops")
.into_response();
assert_eq!(res.status(), 500);
}
}