use crate::{AxumSessionData, AxumSessionID, AxumSessionStore};
use async_trait::async_trait;
use axum_core::extract::{FromRequest, RequestParts};
use http::{self, StatusCode};
use serde::de::DeserializeOwned;
use serde::Serialize;
#[derive(Debug, Clone)]
pub struct AxumSession {
pub(crate) store: AxumSessionStore,
pub(crate) id: AxumSessionID,
}
#[async_trait]
impl<B> FromRequest<B> for AxumSession
where
B: Send,
{
type Rejection = (http::StatusCode, &'static str);
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let extensions = req.extensions().ok_or((
StatusCode::INTERNAL_SERVER_ERROR,
"Can't extract AxumSession: extensions has been taken by another extractor",
))?;
extensions.get::<AxumSession>().cloned().ok_or((
StatusCode::INTERNAL_SERVER_ERROR,
"Can't extract AxumSession. Is `AxumSessionLayer` enabled?",
))
}
}
impl AxumSession {
pub async fn tap<T: DeserializeOwned>(
&self,
func: impl FnOnce(&mut AxumSessionData) -> Option<T>,
) -> Option<T> {
let store_rg = self.store.inner.read().await;
let mut instance = store_rg
.get(&self.id.0.to_string())
.expect("Session data unexpectedly missing")
.lock()
.await;
func(&mut instance)
}
pub async fn destroy(&self) {
self.tap(|sess| {
sess.destroy = true;
Some(1)
})
.await;
}
pub async fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T> {
self.tap(|sess| {
let string = sess.data.get(key)?;
serde_json::from_str(string).ok()
})
.await
}
pub async fn set(&self, key: &str, value: impl Serialize) {
let value = serde_json::to_string(&value).unwrap_or_else(|_| "".to_string());
self.tap(|sess| {
if sess.data.get(key) != Some(&value) {
sess.data.insert(key.to_string(), value);
}
Some(1)
})
.await;
}
pub async fn remove(&self, key: &str) {
self.tap(|sess| sess.data.remove(key)).await;
}
pub async fn clear_all(&self) {
let store_rg = self.store.inner.read().await;
let mut sess = store_rg
.get(&self.id.0.to_string())
.expect("Session data unexpectedly missing")
.lock()
.await;
sess.data.clear();
if self.store.is_persistent() {
self.store.clear_store().await.unwrap();
}
}
pub async fn count(&self) -> i64 {
if self.store.is_persistent() {
self.store.count().await.unwrap_or(0i64)
} else {
self.store.inner.read().await.len() as i64
}
}
}