use axol_http::{
RequestPartsRef,
header::{COOKIE, HeaderMap, SET_COOKIE},
response::ResponsePartsRef,
};
pub use cookie::{Cookie, Expiration, SameSite};
use crate::{FromRequestParts, IntoResponseParts, Result};
#[derive(Debug, Default, Clone)]
pub struct CookieJar {
jar: cookie::CookieJar,
}
#[async_trait::async_trait]
impl<'a> FromRequestParts<'a> for CookieJar {
async fn from_request_parts(request: RequestPartsRef<'a>) -> Result<Self> {
Ok(Self::from_headers(request.headers))
}
}
fn cookies_from_request(headers: &HeaderMap) -> impl Iterator<Item = Cookie<'static>> + '_ {
headers
.get_all(COOKIE)
.flat_map(|value| value.split(';'))
.filter_map(|cookie| Cookie::parse_encoded(cookie.to_owned()).ok())
}
impl CookieJar {
pub fn from_headers(headers: &HeaderMap) -> Self {
let mut jar = cookie::CookieJar::new();
for cookie in cookies_from_request(headers) {
jar.add_original(cookie);
}
Self { jar }
}
pub fn new() -> Self {
Self::default()
}
pub fn get(&self, name: &str) -> Option<&Cookie<'static>> {
self.jar.get(name)
}
#[must_use]
pub fn remove(mut self, cookie: Cookie<'static>) -> Self {
self.jar.remove(cookie);
self
}
#[must_use]
#[allow(clippy::should_implement_trait)]
pub fn add(mut self, cookie: Cookie<'static>) -> Self {
self.jar.add(cookie);
self
}
pub fn iter(&self) -> impl Iterator<Item = &'_ Cookie<'static>> {
self.jar.iter()
}
}
impl IntoResponseParts for CookieJar {
fn into_response_parts(self, res: &mut ResponsePartsRef<'_>) -> Result<()> {
set_cookies(self.jar, res.headers);
Ok(())
}
}
fn set_cookies(jar: cookie::CookieJar, headers: &mut HeaderMap) {
for cookie in jar.delta() {
headers.append(SET_COOKIE, cookie.encoded().to_string());
}
}