use crate::{private::SigningKey, FlashMessage, Level, COOKIE_NAME};
use async_trait::async_trait;
use axum_core::extract::{FromRequest, RequestParts};
use cookie::CookieJar;
use http::StatusCode;
use tower_cookies::{Cookie, Cookies};
#[derive(Debug)]
pub struct IncomingFlashes {
flashes: Vec<FlashMessage>,
}
impl IncomingFlashes {
pub fn iter(&self) -> Iter<'_> {
Iter(self.flashes.iter())
}
pub fn len(&self) -> usize {
self.flashes.len()
}
pub fn is_empty(&self) -> bool {
self.flashes.is_empty()
}
}
#[derive(Debug)]
pub struct Iter<'a>(std::slice::Iter<'a, FlashMessage>);
impl<'a> Iterator for Iter<'a> {
type Item = (Level, &'a str);
fn next(&mut self) -> Option<Self::Item> {
let message = self.0.next()?;
Some((message.level, &message.message))
}
}
impl<'a> IntoIterator for &'a IncomingFlashes {
type Item = (Level, &'a str);
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl IntoIterator for IncomingFlashes {
type Item = (Level, String);
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
IntoIter(self.flashes.into_iter())
}
}
#[async_trait]
impl<B> FromRequest<B> for IncomingFlashes
where
B: Send,
{
type Rejection = (StatusCode, &'static str);
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let cookies = Cookies::from_request(req).await?;
let SigningKey(signing_key) = SigningKey::from_request(req).await?;
let flashes = cookies
.get(COOKIE_NAME)
.map(Cookie::into_owned)
.and_then(|cookie| {
let mut cookie_jar = CookieJar::new();
cookie_jar.add_original(cookie);
cookie_jar.signed(&signing_key).get(COOKIE_NAME)
})
.and_then(|cookie| serde_json::from_str::<Vec<FlashMessage>>(cookie.value()).ok())
.unwrap_or_default();
cookies.remove(Cookie::named(COOKIE_NAME));
Ok(Self { flashes })
}
}
#[derive(Debug)]
pub struct IntoIter(std::vec::IntoIter<FlashMessage>);
impl Iterator for IntoIter {
type Item = (Level, String);
fn next(&mut self) -> Option<Self::Item> {
let message = self.0.next()?;
Some((message.level, message.message))
}
}