use std::{
collections::HashMap,
fmt::Debug,
hash::Hash,
sync::{Arc, RwLock},
};
use chrono::{DateTime, Duration, Utc};
use derive_builder::Builder;
use mincat_core::error::Error;
use super::sess::SessionStore;
#[derive(Debug, Eq, Clone)]
struct SessionKey {
key: String,
exp: DateTime<Utc>,
}
impl SessionKey {
fn new(key: &str, age: i64) -> Self {
let dur = Duration::seconds(age);
Self {
key: key.to_string(),
exp: Utc::now() + dur,
}
}
}
impl Hash for SessionKey {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.key.hash(state);
}
}
impl PartialEq for SessionKey {
fn eq(&self, other: &Self) -> bool {
self.key == other.key
}
}
#[derive(Clone, Debug, Builder)]
pub struct MemorySession {
#[builder(setter(skip))]
store: Arc<RwLock<HashMap<SessionKey, HashMap<String, String>>>>,
#[builder(default = "3600")]
age: i64,
#[builder(default = "60")]
interval: u64,
}
impl MemorySession {
fn session_key(&self, session_id: &str) -> SessionKey {
SessionKey::new(session_id, self.age)
}
}
#[async_trait::async_trait]
impl SessionStore for MemorySession {
async fn has_session(&self, session_id: &str) -> Result<bool, Error> {
let store = self.store.read().map_err(|e| Error::new(e.to_string()))?;
let kv = store.get_key_value(&self.session_key(session_id));
match kv {
Some((k, _)) => {
if Utc::now() > k.exp {
Ok(false)
} else {
Ok(true)
}
}
None => Ok(false),
}
}
async fn register_key(&self, session_id: &str) -> Result<(), Error> {
let mut store = self.store.write().map_err(|e| Error::new(e.to_string()))?;
store.insert(self.session_key(session_id), HashMap::new());
Ok(())
}
async fn set(&self, session_id: &str, key: &str, value: &str) -> Result<(), Error> {
let mut store = self.store.write().map_err(|e| Error::new(e.to_string()))?;
let hm = store
.entry(self.session_key(session_id))
.or_insert(HashMap::new());
hm.insert(key.to_string(), value.to_string());
Ok(())
}
async fn get(&self, session_id: &str, key: &str) -> Result<Option<String>, Error> {
let store = self.store.read().map_err(|e| Error::new(e.to_string()))?;
match store.get(&self.session_key(session_id)) {
Some(hm) => Ok(hm.get(key).map(|e| e.to_owned())),
None => Ok(None),
}
}
async fn delete_exp(&self) -> Result<(), Error> {
let mut store = self.store.write().map_err(|e| Error::new(e.to_string()))?;
let now = Utc::now();
let mut exp_session_keys = vec![];
for session_key in store.keys() {
if now > session_key.exp {
exp_session_keys.push(session_key.clone());
}
}
for session_key in exp_session_keys {
store.remove(&session_key);
}
Ok(())
}
async fn update_exp(&self, session_id: &str) -> Result<(), Error> {
let mut store = self.store.write().map_err(|e| Error::new(e.to_string()))?;
let value = store.get(&self.session_key(session_id)).cloned();
if let Some(value) = value {
let session_key = self.session_key(session_id);
store.remove(&session_key);
store.insert(session_key, value.clone());
}
Ok(())
}
fn get_delete_exp_task_boot_tag(&self) -> bool {
true
}
fn get_delete_exp_task_interval(&self) -> u64 {
self.interval
}
fn clone_box(&self) -> Box<dyn SessionStore> {
Box::new(self.clone())
}
}