use super::{field::Field, r, reply::Reply, Response, R};
use headers::{Cookie, Header, HeaderMapExt};
use http::{header::HeaderName, HeaderMap, HeaderValue};
use hyper::StatusCode;
use thiserror::Error;
#[derive(Clone, Debug, Error)]
#[error("header {:?} missing or malformed", .0)]
pub struct HeaderError(HeaderName);
impl Reply for HeaderError {
#[inline]
fn into_response(self) -> Response {
hyper::Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(format!("{}", self).into())
.unwrap()
}
}
pub fn header(
name: HeaderName,
) -> impl Fn(R![HeaderMap]) -> Result<R![HeaderValue, HeaderMap], HeaderError> {
move |cx| match cx.head.get(&name).cloned() {
Some(h) => Ok(cx.prepend(h)),
None => Err(HeaderError(name.clone())),
}
}
pub fn header_opt(
name: HeaderName,
) -> impl Fn(R![HeaderMap]) -> R![Option<HeaderValue>, HeaderMap] {
move |cx| {
let h = cx.head.get(&name).cloned();
cx.prepend(h)
}
}
pub fn typed_header<H: Header>(cx: R![HeaderMap]) -> Result<R![H, HeaderMap], HeaderError> {
match cx.head.typed_get() {
Some(h) => Ok(cx.prepend(h)),
None => Err(HeaderError(H::name().clone())),
}
}
pub fn typed_header_opt<H: Header>(cx: R![HeaderMap]) -> R![Option<H>, HeaderMap] {
let h = cx.head.typed_get();
cx.prepend(h)
}
#[derive(Clone, Debug, Error)]
pub enum CookieError {
#[error("missing Cookie header")]
MissingHeader,
#[error("missing cookie {:?}", .0)]
MissingCookie(&'static str),
}
impl Reply for CookieError {
#[inline]
fn into_response(self) -> Response {
hyper::Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(format!("{}", self).into())
.unwrap()
}
}
pub fn cookie<const NAME: &'static str>(
cx: R![HeaderMap],
) -> Result<R![Field<String, NAME>, HeaderMap], CookieError> {
let cookie = (cx.head)
.typed_get::<Cookie>()
.ok_or(CookieError::MissingHeader)?;
match cookie.get(NAME).map(|v| v.to_owned()) {
Some(val) => Ok(cx.prepend(val.into())),
None => Err(CookieError::MissingCookie(NAME)),
}
}
pub fn cookie_opt<const NAME: &'static str>(
cx: R![HeaderMap],
) -> R![Field<Option<String>, NAME>, HeaderMap] {
let cookie = (cx.head)
.typed_get::<Cookie>()
.and_then(|c| c.get(NAME).map(|v| v.to_owned()));
cx.prepend(cookie.into())
}
pub fn rename<T, const OLD: &'static str, const NEW: &'static str>(
cx: R![Field<T, OLD>],
) -> R![
Field<T, NEW>
] {
r![cx.head.into_inner().into()]
}
pub fn drop<T>(_: R![T]) -> R![] {
r![]
}