196 lines
4.8 KiB
Rust
196 lines
4.8 KiB
Rust
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<RefCell<SessionInner>>);
|
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
|
pub enum SessionStatus {
|
|
Changed,
|
|
Purged,
|
|
Renewed,
|
|
#[default]
|
|
Unchanged,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct SessionInner {
|
|
state: Map<String, Value>,
|
|
status: SessionStatus,
|
|
}
|
|
|
|
impl Session {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
pub fn from_state(state: Map<String, Value>) -> Self {
|
|
Self(Rc::new(RefCell::new(SessionInner {
|
|
state,
|
|
status: SessionStatus::Unchanged,
|
|
})))
|
|
}
|
|
|
|
pub fn get<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, 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<String, Value>> {
|
|
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<T: Serialize>(&self, key: impl Into<String>, 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<T: Serialize + DeserializeOwned, F>(
|
|
&self,
|
|
key: impl Into<String>,
|
|
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<T: Serialize + DeserializeOwned, F>(
|
|
&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<Value> {
|
|
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<T: DeserializeOwned>(&self, key: &str) -> Option<Result<T, AppError>> {
|
|
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<Uuid> {
|
|
self.get::<Uuid>(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<String, Value>;
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct SessionUser(pub Uuid);
|