actix_cloud/session/
session.rs1use std::{
2 cell::{Ref, RefCell},
3 mem,
4 rc::Rc,
5};
6
7use actix_utils::future::{ready, Ready};
8use actix_web::{
9 dev::{Extensions, Payload, ServiceRequest, ServiceResponse},
10 error::Error,
11 FromRequest, HttpMessage, HttpRequest,
12};
13use serde::{de::DeserializeOwned, Serialize};
14use serde_json::{Map, Value};
15
16use crate::Result;
17
18#[derive(Clone)]
28pub struct Session(Rc<RefCell<SessionInner>>);
29
30#[derive(Debug, Clone, Default, PartialEq, Eq)]
32pub enum SessionStatus {
33 Changed,
35
36 Purged,
41
42 Renewed,
47
48 #[default]
50 Unchanged,
51}
52
53#[derive(Default)]
54struct SessionInner {
55 state: Map<String, Value>,
56 status: SessionStatus,
57}
58
59impl Session {
60 pub fn get<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>> {
64 if let Some(value) = self.0.borrow().state.get(key) {
65 Ok(Some(serde_json::from_value::<T>(value.clone())?))
66 } else {
67 Ok(None)
68 }
69 }
70
71 pub fn contains_key(&self, key: &str) -> bool {
73 self.0.borrow().state.contains_key(key)
74 }
75
76 pub fn entries(&self) -> Ref<'_, Map<String, Value>> {
80 Ref::map(self.0.borrow(), |inner| &inner.state)
81 }
82
83 pub fn status(&self) -> SessionStatus {
85 Ref::map(self.0.borrow(), |inner| &inner.status).clone()
86 }
87
88 pub fn insert<T: Serialize>(&self, key: impl Into<String>, value: T) -> Result<()> {
97 let mut inner = self.0.borrow_mut();
98
99 if inner.status != SessionStatus::Purged {
100 if inner.status != SessionStatus::Renewed {
101 inner.status = SessionStatus::Changed;
102 }
103
104 let key = key.into();
105 let val = serde_json::to_value(&value)?;
106
107 inner.state.insert(key, val);
108 }
109
110 Ok(())
111 }
112
113 pub fn update<T: Serialize + DeserializeOwned, F>(
125 &self,
126 key: impl Into<String>,
127 updater: F,
128 ) -> Result<()>
129 where
130 F: FnOnce(T) -> T,
131 {
132 let mut inner = self.0.borrow_mut();
133 let key_str = key.into();
134
135 if let Some(val) = inner.state.get(&key_str) {
136 let value = serde_json::from_value(val.clone())?;
137
138 let val = serde_json::to_value(updater(value))?;
139
140 inner.state.insert(key_str, val);
141 }
142
143 Ok(())
144 }
145
146 pub fn update_or<T: Serialize + DeserializeOwned, F>(
158 &self,
159 key: &str,
160 default_value: T,
161 updater: F,
162 ) -> Result<()>
163 where
164 F: FnOnce(T) -> T,
165 {
166 if self.contains_key(key) {
167 self.update(key, updater)
168 } else {
169 self.insert(key, default_value)
170 }
171 }
172
173 pub fn remove(&self, key: &str) -> Option<Value> {
177 let mut inner = self.0.borrow_mut();
178
179 if inner.status != SessionStatus::Purged {
180 if inner.status != SessionStatus::Renewed {
181 inner.status = SessionStatus::Changed;
182 }
183 return inner.state.remove(key);
184 }
185
186 None
187 }
188
189 pub fn remove_as<T: DeserializeOwned>(&self, key: &str) -> Option<Result<T, Value>> {
194 self.remove(key)
195 .map(|value| match serde_json::from_value::<T>(value.clone()) {
196 Ok(val) => Ok(val),
197 Err(_err) => Err(value),
198 })
199 }
200
201 pub fn clear(&self) {
203 let mut inner = self.0.borrow_mut();
204
205 if inner.status != SessionStatus::Purged {
206 if inner.status != SessionStatus::Renewed {
207 inner.status = SessionStatus::Changed;
208 }
209 inner.state.clear()
210 }
211 }
212
213 pub fn purge(&self) {
215 let mut inner = self.0.borrow_mut();
216 inner.status = SessionStatus::Purged;
217 inner.state.clear();
218 }
219
220 pub fn renew(&self) {
222 let mut inner = self.0.borrow_mut();
223
224 if inner.status != SessionStatus::Purged {
225 inner.status = SessionStatus::Renewed;
226 }
227 }
228
229 #[allow(clippy::needless_pass_by_ref_mut)]
234 pub(crate) fn set_session(
235 req: &mut ServiceRequest,
236 data: impl IntoIterator<Item = (String, Value)>,
237 ) {
238 let session = Session::get_session(&mut req.extensions_mut());
239 let mut inner = session.0.borrow_mut();
240 inner.state.extend(data);
241 }
242
243 #[allow(clippy::needless_pass_by_ref_mut)]
249 pub(crate) fn get_changes<B>(
250 res: &mut ServiceResponse<B>,
251 ) -> (SessionStatus, Map<String, Value>) {
252 if let Some(s_impl) = res
253 .request()
254 .extensions()
255 .get::<Rc<RefCell<SessionInner>>>()
256 {
257 let state = mem::take(&mut s_impl.borrow_mut().state);
258 (s_impl.borrow().status.clone(), state)
259 } else {
260 (SessionStatus::Unchanged, Map::new())
261 }
262 }
263
264 pub(crate) fn get_session(extensions: &mut Extensions) -> Session {
265 if let Some(s_impl) = extensions.get::<Rc<RefCell<SessionInner>>>() {
266 return Session(Rc::clone(s_impl));
267 }
268
269 let inner = Rc::new(RefCell::new(SessionInner::default()));
270 extensions.insert(inner.clone());
271
272 Session(inner)
273 }
274}
275
276impl FromRequest for Session {
278 type Error = Error;
279 type Future = Ready<Result<Session, Error>>;
280
281 #[inline]
282 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
283 ready(Ok(Session::get_session(&mut req.extensions_mut())))
284 }
285}