use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
use uuid::Uuid;
pub type SessionId = String;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
pub data: HashMap<String, serde_json::Value>,
}
impl Session {
pub fn new() -> Self {
Self {
data: HashMap::new(),
}
}
pub fn set(&mut self, key: impl Into<String>, value: serde_json::Value) {
self.data.insert(key.into(), value);
}
pub fn get(&self, key: &str) -> Option<&serde_json::Value> {
self.data.get(key)
}
pub fn remove(&mut self, key: &str) -> Option<serde_json::Value> {
self.data.remove(key)
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn clear(&mut self) {
self.data.clear();
}
}
impl Default for Session {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
pub trait SessionStore: Send + Sync {
async fn load(&self, session_id: &SessionId) -> Option<Session>;
async fn save(&self, session_id: &SessionId, session: &Session);
async fn delete(&self, session_id: &SessionId);
fn create_session_id(&self) -> SessionId {
Uuid::new_v4().to_string()
}
}
pub struct InMemorySessionStore {
sessions: Arc<Mutex<HashMap<SessionId, Session>>>,
}
impl InMemorySessionStore {
pub fn new() -> Self {
Self {
sessions: Arc::new(Mutex::new(HashMap::new())),
}
}
}
impl Default for InMemorySessionStore {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl SessionStore for InMemorySessionStore {
async fn load(&self, session_id: &SessionId) -> Option<Session> {
let sessions = self.sessions.lock().await;
sessions.get(session_id).cloned()
}
async fn save(&self, session_id: &SessionId, session: &Session) {
let mut sessions = self.sessions.lock().await;
sessions.insert(session_id.clone(), session.clone());
}
async fn delete(&self, session_id: &SessionId) {
let mut sessions = self.sessions.lock().await;
sessions.remove(session_id);
}
}
pub const SESSION_KEY_USER_ID: &str = "_auth_user_id";
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_session_new() {
let session = Session::new();
assert!(session.is_empty());
}
#[test]
fn test_session_set_get() {
let mut session = Session::new();
session.set("key", serde_json::json!("value"));
assert_eq!(session.get("key"), Some(&serde_json::json!("value")));
}
#[test]
fn test_session_remove() {
let mut session = Session::new();
session.set("key", serde_json::json!("value"));
assert_eq!(session.remove("key"), Some(serde_json::json!("value")));
assert!(session.is_empty());
}
#[test]
fn test_session_clear() {
let mut session = Session::new();
session.set("key1", serde_json::json!("value1"));
session.set("key2", serde_json::json!("value2"));
session.clear();
assert!(session.is_empty());
}
#[tokio::test]
async fn test_in_memory_session_store() {
let store = InMemorySessionStore::new();
let session_id = store.create_session_id();
let mut session = Session::new();
session.set("user_id", serde_json::json!("123"));
store.save(&session_id, &session).await;
let loaded = store.load(&session_id).await;
assert!(loaded.is_some());
assert_eq!(
loaded.unwrap().get("user_id"),
Some(&serde_json::json!("123"))
);
store.delete(&session_id).await;
assert!(store.load(&session_id).await.is_none());
}
#[tokio::test]
async fn test_session_store_create_session_id() {
let store = InMemorySessionStore::new();
let id1 = store.create_session_id();
let id2 = store.create_session_id();
assert_ne!(id1, id2);
assert!(!id1.is_empty());
assert!(!id2.is_empty());
}
}