ferro_rs/session/
store.rs1use async_trait::async_trait;
4use serde::{de::DeserializeOwned, Serialize};
5use std::collections::HashMap;
6
7use crate::error::FrameworkError;
8
9#[derive(Clone, Debug, Default)]
13pub struct SessionData {
14 pub id: String,
16 pub data: HashMap<String, serde_json::Value>,
18 pub user_id: Option<i64>,
20 pub csrf_token: String,
22 pub dirty: bool,
24}
25
26impl SessionData {
27 pub fn new(id: String, csrf_token: String) -> Self {
29 Self {
30 id,
31 data: HashMap::new(),
32 user_id: None,
33 csrf_token,
34 dirty: false,
35 }
36 }
37
38 pub fn get<T: DeserializeOwned>(&self, key: &str) -> Option<T> {
46 self.data
47 .get(key)
48 .and_then(|v| serde_json::from_value(v.clone()).ok())
49 }
50
51 pub fn put<T: Serialize>(&mut self, key: &str, value: T) {
60 if let Ok(v) = serde_json::to_value(value) {
61 self.data.insert(key.to_string(), v);
62 self.dirty = true;
63 }
64 }
65
66 pub fn forget(&mut self, key: &str) -> Option<serde_json::Value> {
70 self.dirty = true;
71 self.data.remove(key)
72 }
73
74 pub fn has(&self, key: &str) -> bool {
76 self.data.contains_key(key)
77 }
78
79 pub fn flash<T: Serialize>(&mut self, key: &str, value: T) {
87 self.put(&format!("_flash.new.{key}"), value);
88 }
89
90 pub fn get_flash<T: DeserializeOwned>(&mut self, key: &str) -> Option<T> {
92 let flash_key = format!("_flash.old.{key}");
93 let value = self.get(&flash_key);
94 if value.is_some() {
95 self.forget(&flash_key);
96 }
97 value
98 }
99
100 pub fn age_flash_data(&mut self) {
102 let old_keys: Vec<String> = self
104 .data
105 .keys()
106 .filter(|k| k.starts_with("_flash.old."))
107 .cloned()
108 .collect();
109 let had_old = !old_keys.is_empty();
110 for key in old_keys {
111 self.data.remove(&key);
112 }
113
114 let new_keys: Vec<String> = self
116 .data
117 .keys()
118 .filter(|k| k.starts_with("_flash.new."))
119 .cloned()
120 .collect();
121 let had_new = !new_keys.is_empty();
122 for key in new_keys {
123 if let Some(value) = self.data.remove(&key) {
124 let old_key = key.replace("_flash.new.", "_flash.old.");
125 self.data.insert(old_key, value);
126 }
127 }
128
129 if had_new || had_old {
130 self.dirty = true;
131 }
132 }
133
134 pub fn flush(&mut self) {
136 self.data.clear();
137 self.user_id = None;
138 self.dirty = true;
139 }
140
141 pub fn is_dirty(&self) -> bool {
143 self.dirty
144 }
145
146 pub fn mark_clean(&mut self) {
148 self.dirty = false;
149 }
150}
151
152#[async_trait]
156pub trait SessionStore: Send + Sync {
157 async fn read(&self, id: &str) -> Result<Option<SessionData>, FrameworkError>;
161
162 async fn write(&self, session: &SessionData) -> Result<(), FrameworkError>;
166
167 async fn destroy(&self, id: &str) -> Result<(), FrameworkError>;
169
170 async fn gc(&self) -> Result<u64, FrameworkError>;
174
175 async fn destroy_for_user(
182 &self,
183 _user_id: i64,
184 _except_session_id: Option<&str>,
185 ) -> Result<u64, FrameworkError> {
186 Err(FrameworkError::internal(
187 "destroy_for_user not supported by this session driver".to_string(),
188 ))
189 }
190}