use std::cell::{Ref, RefCell}; use std::rc::Rc; use serde::Serialize; use serde::de::DeserializeOwned; use serde_json::{Map, Value}; use uuid::Uuid; use crate::error::AppError; const SESSION_USER_KEY: &str = "session:user_uid"; #[derive(Clone)] pub struct Session(Rc>); #[derive(Debug, Clone, Default, PartialEq, Eq)] pub enum SessionStatus { Changed, Purged, Renewed, #[default] Unchanged, } #[derive(Default)] struct SessionInner { state: Map, status: SessionStatus, } impl Session { pub fn new() -> Self { Self::default() } pub fn from_state(state: Map) -> Self { Self(Rc::new(RefCell::new(SessionInner { state, status: SessionStatus::Unchanged, }))) } pub fn get(&self, key: &str) -> Result, AppError> { if let Some(value) = self.0.borrow().state.get(key) { Ok(Some(serde_json::from_value(value.clone())?)) } else { Ok(None) } } pub fn contains_key(&self, key: &str) -> bool { self.0.borrow().state.contains_key(key) } pub fn entries(&self) -> Ref<'_, Map> { Ref::map(self.0.borrow(), |inner| &inner.state) } pub fn status(&self) -> SessionStatus { Ref::map(self.0.borrow(), |inner| &inner.status).clone() } pub fn insert(&self, key: impl Into, value: T) -> Result<(), AppError> { let mut inner = self.0.borrow_mut(); if inner.status != SessionStatus::Purged { if inner.status != SessionStatus::Renewed { inner.status = SessionStatus::Changed; } let val = serde_json::to_value(&value)?; inner.state.insert(key.into(), val); } Ok(()) } pub fn update( &self, key: impl Into, updater: F, ) -> Result<(), AppError> where F: FnOnce(T) -> T, { let mut inner = self.0.borrow_mut(); let key_str = key.into(); if let Some(val) = inner.state.get(&key_str) { if inner.status == SessionStatus::Purged { return Ok(()); } let value: T = serde_json::from_value(val.clone())?; let updated = serde_json::to_value(updater(value))?; if inner.status != SessionStatus::Renewed { inner.status = SessionStatus::Changed; } inner.state.insert(key_str, updated); } Ok(()) } pub fn update_or( &self, key: &str, default: T, updater: F, ) -> Result<(), AppError> where F: FnOnce(T) -> T, { if self.contains_key(key) { self.update(key, updater) } else { self.insert(key, default) } } pub fn remove(&self, key: &str) -> Option { let mut inner = self.0.borrow_mut(); if inner.status != SessionStatus::Purged { if inner.status != SessionStatus::Renewed { inner.status = SessionStatus::Changed; } return inner.state.remove(key); } None } pub fn remove_as(&self, key: &str) -> Option> { self.remove(key) .map(|value| serde_json::from_value(value).map_err(AppError::Json)) } pub fn clear(&self) { let mut inner = self.0.borrow_mut(); if inner.status != SessionStatus::Purged { if inner.status != SessionStatus::Renewed { inner.status = SessionStatus::Changed; } inner.state.clear(); } } pub fn purge(&self) { let mut inner = self.0.borrow_mut(); inner.status = SessionStatus::Purged; inner.state.clear(); } pub fn renew(&self) { let mut inner = self.0.borrow_mut(); if inner.status != SessionStatus::Purged { inner.status = SessionStatus::Renewed; } } pub fn user(&self) -> Option { self.get::(SESSION_USER_KEY).ok().flatten() } pub fn set_user(&self, uid: Uuid) { let _ = self.insert(SESSION_USER_KEY, uid); } pub fn clear_user(&self) { let _ = self.remove(SESSION_USER_KEY); } pub fn take_state(&self) -> SessionState { let mut inner = self.0.borrow_mut(); std::mem::take(&mut inner.state) } pub fn mark_unchanged(&self) { self.0.borrow_mut().status = SessionStatus::Unchanged; } } impl Default for Session { fn default() -> Self { Self(Rc::new(RefCell::new(SessionInner::default()))) } } pub type SessionState = Map; #[derive(Debug, Clone, Copy)] pub struct SessionUser(pub Uuid);