use async_trait::async_trait;
use reinhardt_http::Request;
use serde::de::DeserializeOwned;
use std::collections::HashMap;
use std::fmt::{self, Debug};
use std::ops::Deref;
use super::{
ParamContext, ParamError, ParamErrorContext, ParamResult, ParamType, extract::FromRequest,
};
pub struct Header<T>(pub T);
impl<T> Header<T> {
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> Deref for Header<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: Debug> Debug for Header<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
pub struct HeaderStruct<T>(pub T);
impl<T> HeaderStruct<T> {
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> Deref for HeaderStruct<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: Debug> Debug for HeaderStruct<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
fn headers_to_map(req: &Request) -> HashMap<String, String> {
let mut result = HashMap::new();
for (name, value) in req.headers.iter() {
if let Ok(value_str) = value.to_str() {
result.insert(name.as_str().to_lowercase(), value_str.to_string());
}
}
result
}
#[async_trait]
impl<T> FromRequest for HeaderStruct<T>
where
T: DeserializeOwned + Send,
{
async fn from_request(req: &Request, _ctx: &ParamContext) -> ParamResult<Self> {
let headers_map = headers_to_map(req);
let encoded = serde_urlencoded::to_string(&headers_map).map_err(|e| {
ParamError::ParseError(Box::new(
ParamErrorContext::new(
ParamType::Header,
format!("Failed to encode headers: {}", e),
)
.with_expected_type::<T>()
.with_source(Box::new(e)),
))
})?;
serde_urlencoded::from_str(&encoded)
.map(HeaderStruct)
.map_err(|e| ParamError::url_encoding::<T>(ParamType::Header, e, Some(encoded.clone())))
}
}
#[async_trait]
impl FromRequest for Header<String> {
async fn from_request(req: &Request, ctx: &ParamContext) -> ParamResult<Self> {
let name = ctx.get_header_name::<String>().ok_or_else(|| {
ParamError::MissingParameter(
"Header name not specified in ParamContext for this type".to_string(),
)
})?;
let value = req
.headers
.get(name)
.and_then(|v| v.to_str().ok())
.ok_or_else(|| ParamError::MissingParameter(name.to_string()))?;
Ok(Header(value.to_string()))
}
}
#[async_trait]
impl FromRequest for Header<Option<String>> {
async fn from_request(req: &Request, ctx: &ParamContext) -> ParamResult<Self> {
let name = match ctx.get_header_name::<String>() {
Some(n) => n,
None => return Ok(Header(None)),
};
let maybe = req.headers.get(name).and_then(|v| v.to_str().ok());
Ok(Header(maybe.map(|s| s.to_string())))
}
}
#[cfg(feature = "validation")]
impl<T> super::validation::WithValidation for Header<T> {}