feat: init

This commit is contained in:
zhenyi
2026-06-07 11:30:56 +08:00
commit 563381c1ca
361 changed files with 41327 additions and 0 deletions
+195
View File
@@ -0,0 +1,195 @@
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);