use async_trait::async_trait;
use reinhardt_http::Request;
use serde::de::DeserializeOwned;
use std::fmt::{self, Debug};
use std::ops::Deref;
use super::{
ParamContext, ParamError, ParamErrorContext, ParamResult, ParamType, extract::FromRequest,
};
pub struct Cookie<T>(pub T);
impl<T> Cookie<T> {
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> Deref for Cookie<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: Debug> Debug for Cookie<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
pub struct CookieStruct<T>(pub T);
impl<T> CookieStruct<T> {
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> Deref for CookieStruct<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: Debug> Debug for CookieStruct<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
use super::cookie_util::parse_cookies;
#[async_trait]
impl<T> FromRequest for CookieStruct<T>
where
T: DeserializeOwned + Send,
{
async fn from_request(req: &Request, _ctx: &ParamContext) -> ParamResult<Self> {
let cookie_header = req
.headers
.get(http::header::COOKIE)
.and_then(|h| h.to_str().ok())
.unwrap_or("");
let cookies_map = parse_cookies(cookie_header);
let json_value = serde_json::to_value(&cookies_map).map_err(|e| {
ParamError::InvalidParameter(Box::new(
ParamErrorContext::new(ParamType::Cookie, e.to_string()).with_field("cookies"),
))
})?;
serde_json::from_value(json_value)
.map(CookieStruct)
.map_err(|e| {
ParamError::InvalidParameter(Box::new(
ParamErrorContext::new(ParamType::Cookie, e.to_string()).with_field("cookies"),
))
})
}
}
#[async_trait]
impl FromRequest for Cookie<String> {
async fn from_request(req: &Request, ctx: &ParamContext) -> ParamResult<Self> {
let name = ctx.get_cookie_name::<String>().ok_or_else(|| {
ParamError::MissingParameter(
"Cookie name not specified in ParamContext for this type".to_string(),
)
})?;
let cookie_header = req
.headers
.get(http::header::COOKIE)
.and_then(|h| h.to_str().ok())
.unwrap_or("");
let cookies_map = parse_cookies(cookie_header);
let value = cookies_map
.get(name)
.ok_or_else(|| ParamError::MissingParameter(name.to_string()))?;
Ok(Cookie(value.to_string()))
}
}
#[async_trait]
impl FromRequest for Cookie<Option<String>> {
async fn from_request(req: &Request, ctx: &ParamContext) -> ParamResult<Self> {
let name = match ctx.get_cookie_name::<String>() {
Some(n) => n,
None => return Ok(Cookie(None)),
};
let cookie_header = req
.headers
.get(http::header::COOKIE)
.and_then(|h| h.to_str().ok())
.unwrap_or("");
let cookies_map = parse_cookies(cookie_header);
Ok(Cookie(cookies_map.get(name).map(|s| s.to_string())))
}
}
#[cfg(feature = "validation")]
impl<T> super::validation::WithValidation for Cookie<T> {}
#[cfg(feature = "validation")]
impl<T> super::validation::WithValidation for CookieStruct<T> {}