tower_sessions_ext_memory_store/
lib.rs1use std::{collections::HashMap, sync::Arc};
2
3use async_trait::async_trait;
4use time::OffsetDateTime;
5use tokio::sync::Mutex;
6use tower_sessions_ext_core::{
7 SessionStore,
8 session::{Id, Record},
9 session_store,
10};
11
12#[derive(Clone, Debug, Default)]
23pub struct MemoryStore(Arc<Mutex<HashMap<Id, Record>>>);
24
25#[async_trait]
26impl SessionStore for MemoryStore {
27 async fn create(&self, record: &mut Record) -> session_store::Result<()> {
28 let mut store_guard = self.0.lock().await;
29 while store_guard.contains_key(&record.id) {
30 record.id = Id::default();
32 }
33 store_guard.insert(record.id, record.clone());
34 Ok(())
35 }
36
37 async fn save(&self, record: &Record) -> session_store::Result<()> {
38 self.0.lock().await.insert(record.id, record.clone());
39 Ok(())
40 }
41
42 async fn load(&self, session_id: &Id) -> session_store::Result<Option<Record>> {
43 Ok(self
44 .0
45 .lock()
46 .await
47 .get(session_id)
48 .filter(|Record { expiry_date, .. }| is_active(*expiry_date))
49 .cloned())
50 }
51
52 async fn delete(&self, session_id: &Id) -> session_store::Result<()> {
53 self.0.lock().await.remove(session_id);
54 Ok(())
55 }
56}
57
58fn is_active(expiry_date: OffsetDateTime) -> bool {
59 expiry_date > OffsetDateTime::now_utc()
60}
61
62#[cfg(test)]
63mod tests {
64 use time::Duration;
65
66 use super::*;
67
68 #[tokio::test]
69 async fn test_create() {
70 let store = MemoryStore::default();
71 let mut record = Record {
72 id: Default::default(),
73 data: Default::default(),
74 expiry_date: OffsetDateTime::now_utc() + Duration::minutes(30),
75 };
76 assert!(store.create(&mut record).await.is_ok());
77 }
78
79 #[tokio::test]
80 async fn test_save() {
81 let store = MemoryStore::default();
82 let record = Record {
83 id: Default::default(),
84 data: Default::default(),
85 expiry_date: OffsetDateTime::now_utc() + Duration::minutes(30),
86 };
87 assert!(store.save(&record).await.is_ok());
88 }
89
90 #[tokio::test]
91 async fn test_load() {
92 let store = MemoryStore::default();
93 let mut record = Record {
94 id: Default::default(),
95 data: Default::default(),
96 expiry_date: OffsetDateTime::now_utc() + Duration::minutes(30),
97 };
98 store.create(&mut record).await.unwrap();
99 let loaded_record = store.load(&record.id).await.unwrap();
100 assert_eq!(Some(record), loaded_record);
101 }
102
103 #[tokio::test]
104 async fn test_delete() {
105 let store = MemoryStore::default();
106 let mut record = Record {
107 id: Default::default(),
108 data: Default::default(),
109 expiry_date: OffsetDateTime::now_utc() + Duration::minutes(30),
110 };
111 store.create(&mut record).await.unwrap();
112 assert!(store.delete(&record.id).await.is_ok());
113 assert_eq!(None, store.load(&record.id).await.unwrap());
114 }
115
116 #[tokio::test]
117 async fn test_create_id_collision() {
118 let store = MemoryStore::default();
119 let expiry_date = OffsetDateTime::now_utc() + Duration::minutes(30);
120 let mut record1 = Record {
121 id: Default::default(),
122 data: Default::default(),
123 expiry_date,
124 };
125 let mut record2 = Record {
126 id: Default::default(),
127 data: Default::default(),
128 expiry_date,
129 };
130 store.create(&mut record1).await.unwrap();
131 record2.id = record1.id; store.create(&mut record2).await.unwrap();
133 assert_ne!(record1.id, record2.id); }
135}