use super::{field::IsoDecode, r, reply::Reply, Response, R};
use bytes::{buf::BufExt, Buf, Bytes};
use frunk_core::hlist::{HCons, HList};
use futures::future::FutureExt;
use headers::{ContentType, HeaderMapExt};
use http::HeaderMap;
use hyper::{body, Body, StatusCode};
use mime::Mime;
use serde::de::DeserializeOwned;
use std::future::Future;
use thiserror::Error;
macro_rules! bad_request_display {
($t: ty) => {
impl Reply for $t {
#[inline]
fn into_response(self) -> Response {
hyper::Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(format!("{}", self).into())
.unwrap()
}
}
};
}
#[derive(Debug, Error)]
pub enum JsonBodyError {
#[error("failed to read body: {}", .0)]
Body(#[from] hyper::Error),
#[error("failed to parse body: {}", .0)]
Json(#[from] serde_json::Error),
}
bad_request_display! { JsonBodyError }
pub async fn json<T: DeserializeOwned>(cx: R![Body]) -> Result<R![T], JsonBodyError> {
let bodyr = body::aggregate(cx.head).await?.reader();
serde_json::from_reader(bodyr)
.map_err(JsonBodyError::Json)
.map(|t| r![t])
}
pub async fn jsonr<T: IsoDecode>(cx: R![Body]) -> Result<T, JsonBodyError> {
let bodyr = body::aggregate(cx.head).await?.reader();
serde_json::from_reader(bodyr)
.map_err(JsonBodyError::Json)
.map(T::from_repr)
}
#[derive(Debug, Error)]
pub enum FormBodyError {
#[error("failed to read body: {}", .0)]
Body(#[from] hyper::Error),
#[error("failed to parse body: {}", .0)]
Form(#[from] serde_urlencoded::de::Error),
}
bad_request_display! { FormBodyError }
pub async fn form<T: DeserializeOwned>(cx: R![Body]) -> Result<R![T], FormBodyError> {
let bodyr = body::aggregate(cx.head).await?.reader();
serde_urlencoded::from_reader(bodyr)
.map_err(FormBodyError::Form)
.map(|t| r![t])
}
pub async fn formr<T: IsoDecode>(cx: R![Body]) -> Result<T, FormBodyError> {
let bodyr = body::aggregate(cx.head).await?.reader();
serde_urlencoded::from_reader(bodyr)
.map_err(FormBodyError::Form)
.map(T::from_repr)
}
#[derive(Debug, Error)]
pub enum AutoBodyError {
#[error("failed to determine format")]
MissingContentType,
#[error("unknown format: {}", .0)]
UnknownFormat(Mime),
#[error("failed to read body: {}", .0)]
Body(#[from] hyper::Error),
#[error("failed to parse body: {}", .0)]
Json(#[from] serde_json::Error),
#[error("failed to parse body: {}", .0)]
Form(#[from] serde_urlencoded::de::Error),
}
bad_request_display! { AutoBodyError }
pub async fn auto<T>(cx: R![Body, HeaderMap]) -> Result<R![T, HeaderMap], AutoBodyError>
where T: DeserializeOwned {
let (head, tail) = (cx.head, cx.tail);
let mime: Mime = (tail.head)
.typed_get::<ContentType>()
.ok_or(AutoBodyError::MissingContentType)?
.into();
let bodyr = body::aggregate(head).await?.reader();
match mime.subtype() {
mime::JSON => serde_json::from_reader(bodyr)
.map_err(AutoBodyError::Json)
.map(|t| tail.prepend(t)),
mime::WWW_FORM_URLENCODED => serde_urlencoded::from_reader(bodyr)
.map_err(AutoBodyError::Form)
.map(|t| tail.prepend(t)),
_ => Err(AutoBodyError::UnknownFormat(mime)),
}
}
pub async fn autor<T>(cx: R![Body, HeaderMap]) -> Result<HCons<HeaderMap, T>, AutoBodyError>
where T: HList + IsoDecode {
let (head, tail) = (cx.head, cx.tail);
let mime: Mime = (tail.head)
.typed_get::<ContentType>()
.ok_or(AutoBodyError::MissingContentType)?
.into();
let bodyr = body::aggregate(head).await?.reader();
match mime.subtype() {
mime::JSON => serde_json::from_reader(bodyr)
.map_err(AutoBodyError::Json)
.map(|t| T::from_repr(t).prepend(tail.head)),
mime::WWW_FORM_URLENCODED => serde_urlencoded::from_reader(bodyr)
.map_err(AutoBodyError::Form)
.map(|t| T::from_repr(t).prepend(tail.head)),
_ => Err(AutoBodyError::UnknownFormat(mime)),
}
}
bad_request_display! { hyper::Error }
pub async fn bytes(cx: R![Body]) -> hyper::Result<R![Bytes]> {
body::to_bytes(cx.head).await.map(|b| r![b])
}
pub fn aggregate(cx: R![Body]) -> impl Future<Output = hyper::Result<R![Buffer]>> {
body::aggregate(cx.head).map(|h| h.map(|b| r![b]))
}
pub type Buffer = impl Buf;