use std::{
fmt,
io::{Error, ErrorKind, Result},
sync::{
atomic::{AtomicU8, Ordering},
Arc, RwLock,
},
};
use serde::{de::DeserializeOwned, Serialize};
use crate::{Data, State, CHANGED, PURGED, RENEWED, UNCHANGED};
#[derive(Clone)]
pub struct Session {
state: Arc<State>,
}
impl Session {
pub fn new(data: Data) -> Self {
Self {
state: Arc::new(State {
status: AtomicU8::new(UNCHANGED),
data: RwLock::new(data),
}),
}
}
pub fn status(&self) -> &AtomicU8 {
&self.state.status
}
pub fn lock_data(&self) -> &RwLock<Data> {
&self.state.data
}
pub fn get<T>(&self, key: &str) -> Result<Option<T>>
where
T: DeserializeOwned,
{
match self
.lock_data()
.read()
.map_err(into_io_error)?
.get(key)
.cloned()
{
Some(t) => serde_json::from_value(t).map(Some).map_err(Into::into),
None => Ok(None),
}
}
pub fn set<T>(&self, key: &str, val: T) -> Result<()>
where
T: Serialize,
{
let status = self.status().load(Ordering::Acquire);
if status != PURGED {
if let Ok(mut d) = self.lock_data().write() {
if status == UNCHANGED {
self.status().store(CHANGED, Ordering::SeqCst);
}
d.insert(
key.into(),
serde_json::to_value(val).map_err(into_io_error)?,
);
}
}
Ok(())
}
pub fn remove(&self, key: &str) -> Option<serde_json::Value> {
let status = self.status().load(Ordering::Acquire);
if status != PURGED {
if let Ok(mut d) = self.lock_data().write() {
if status == UNCHANGED {
self.status().store(CHANGED, Ordering::SeqCst);
}
return d.remove(key);
}
}
None
}
pub fn remove_as<T>(&self, key: &str) -> Option<T>
where
T: DeserializeOwned,
{
serde_json::from_value(self.remove(key)?).ok()
}
pub fn clear(&self) {
let status = self.status().load(Ordering::Acquire);
if status != PURGED {
if let Ok(mut d) = self.lock_data().write() {
if status == UNCHANGED {
self.status().store(CHANGED, Ordering::SeqCst);
}
d.clear();
}
}
}
pub fn renew(&self) {
let status = self.status().load(Ordering::Acquire);
if status != PURGED && status != RENEWED {
self.status().store(RENEWED, Ordering::SeqCst)
}
}
pub fn purge(&self) {
let status = self.status().load(Ordering::Acquire);
if status != PURGED {
self.status().store(PURGED, Ordering::SeqCst);
if let Ok(mut d) = self.lock_data().write() {
d.clear();
}
}
}
pub fn data(&self) -> Result<Data> {
self.lock_data()
.read()
.map_err(into_io_error)
.map(|d| d.clone())
}
}
impl fmt::Debug for Session {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.state.fmt(f)
}
}
#[inline]
fn into_io_error<E: std::error::Error>(e: E) -> Error {
Error::new(ErrorKind::Other, e.to_string())
}