1use parking_lot::RwLock;
39use serde_json::Value;
40use std::collections::HashMap;
41use std::sync::Arc;
42
43pub trait SessionStore: Send + Sync {
63 fn read(&self, session_id: &str) -> Option<HashMap<String, Value>>;
67
68 fn write(&self, session_id: &str, data: HashMap<String, Value>);
72
73 fn destroy(&self, session_id: &str);
77
78 fn exists(&self, session_id: &str) -> bool {
80 self.read(session_id).is_some()
81 }
82}
83
84#[derive(Debug, Clone, Default)]
92pub struct MemorySessionStore {
93 data: Arc<RwLock<HashMap<String, HashMap<String, Value>>>>,
94}
95
96impl MemorySessionStore {
97 pub fn new() -> Self {
99 Self::default()
100 }
101}
102
103impl SessionStore for MemorySessionStore {
104 fn read(&self, session_id: &str) -> Option<HashMap<String, Value>> {
105 self.data.read().get(session_id).cloned()
106 }
107
108 fn write(&self, session_id: &str, data: HashMap<String, Value>) {
109 self.data.write().insert(session_id.to_string(), data);
110 }
111
112 fn destroy(&self, session_id: &str) {
113 self.data.write().remove(session_id);
114 }
115
116 fn exists(&self, session_id: &str) -> bool {
117 self.data.read().contains_key(session_id)
118 }
119}
120
121const FLASH_PREFIX: &str = "__flash__:";
130
131pub struct Session {
150 session_id: String,
152 prefix: String,
154 store: Arc<dyn SessionStore>,
156}
157
158impl Session {
159 pub fn new(session_id: impl Into<String>, store: impl SessionStore + 'static) -> Self {
166 Self {
167 session_id: session_id.into(),
168 prefix: String::new(),
169 store: Arc::new(store),
170 }
171 }
172
173 pub fn with_shared_store(session_id: impl Into<String>, store: Arc<dyn SessionStore>) -> Self {
175 Self {
176 session_id: session_id.into(),
177 prefix: String::new(),
178 store,
179 }
180 }
181
182 #[must_use]
187 pub fn with_prefix(mut self, prefix: impl Into<String>) -> Self {
188 self.prefix = prefix.into();
189 self
190 }
191
192 pub fn session_id(&self) -> &str {
194 &self.session_id
195 }
196
197 fn full_key(&self, name: &str) -> String {
199 if self.prefix.is_empty() {
200 name.to_string()
201 } else {
202 format!("{}{}", self.prefix, name)
203 }
204 }
205
206 pub fn set(&self, name: &str, value: Value) {
214 let mut data = self.store.read(&self.session_id).unwrap_or_default();
215 data.insert(self.full_key(name), value);
216 self.store.write(&self.session_id, data);
217 }
218
219 pub fn get(&self, name: &str) -> Option<Value> {
226 let data = self.store.read(&self.session_id)?;
227 data.get(&self.full_key(name)).cloned()
228 }
229
230 pub fn get_with_default(&self, name: &str, default: Value) -> Value {
234 self.get(name).unwrap_or(default)
235 }
236
237 pub fn has(&self, name: &str) -> bool {
239 self.get(name).is_some()
240 }
241
242 pub fn delete(&self, name: &str) -> Option<Value> {
246 let mut data = self.store.read(&self.session_id)?;
247 let key = self.full_key(name);
248 let removed = data.remove(&key);
249 self.store.write(&self.session_id, data);
250 removed
251 }
252
253 pub fn clear(&self) {
257 self.store.destroy(&self.session_id);
258 }
259
260 pub fn flash(&self, name: &str, value: Value) {
269 let flash_key = format!("{}{}", FLASH_PREFIX, name);
270 self.set(&flash_key, value);
271 }
272
273 pub fn get_flash(&self, name: &str) -> Option<Value> {
277 let flash_key = format!("{}{}", FLASH_PREFIX, name);
278 self.get(&flash_key)
279 }
280
281 pub fn clear_flash(&self) {
285 let mut data = match self.store.read(&self.session_id) {
286 Some(d) => d,
287 None => return,
288 };
289 let flash_keys: Vec<String> = data
291 .keys()
292 .filter(|k| k.starts_with(FLASH_PREFIX))
293 .cloned()
294 .collect();
295 for key in flash_keys {
296 data.remove(&key);
297 }
298 self.store.write(&self.session_id, data);
299 }
300
301 pub fn flush(&self) {
306 self.clear();
307 }
308
309 pub fn all(&self) -> HashMap<String, Value> {
313 let data = self.store.read(&self.session_id).unwrap_or_default();
314 data.into_iter()
316 .filter(|(k, _)| !k.starts_with(FLASH_PREFIX))
317 .collect()
318 }
319}
320
321#[cfg(test)]
326mod tests {
327 use super::*;
328 use serde_json::json;
329
330 #[test]
335 fn test_memory_store_write_read_roundtrip() {
336 let store = MemorySessionStore::new();
337 let mut data = HashMap::new();
338 data.insert("user_id".to_string(), json!(12345));
339 data.insert("name".to_string(), json!("alice"));
340
341 store.write("session-1", data.clone());
342 let read = store.read("session-1").unwrap();
343 assert_eq!(read.len(), 2);
344 assert_eq!(read.get("user_id"), Some(&json!(12345)));
345 assert_eq!(read.get("name"), Some(&json!("alice")));
346 }
347
348 #[test]
349 fn test_memory_store_read_nonexistent_returns_none() {
350 let store = MemorySessionStore::new();
351 assert!(store.read("nonexistent").is_none());
352 }
353
354 #[test]
355 fn test_memory_store_destroy() {
356 let store = MemorySessionStore::new();
357 let data = HashMap::new();
358 store.write("session-1", data);
359 assert!(store.exists("session-1"));
360
361 store.destroy("session-1");
362 assert!(!store.exists("session-1"));
363 }
364
365 #[test]
366 fn test_memory_store_isolated_by_session_id() {
367 let store = MemorySessionStore::new();
368 let mut data1 = HashMap::new();
369 data1.insert("user".to_string(), json!("alice"));
370 store.write("session-1", data1);
371
372 let mut data2 = HashMap::new();
373 data2.insert("user".to_string(), json!("bob"));
374 store.write("session-2", data2);
375
376 assert_eq!(
377 store.read("session-1").unwrap().get("user"),
378 Some(&json!("alice"))
379 );
380 assert_eq!(
381 store.read("session-2").unwrap().get("user"),
382 Some(&json!("bob"))
383 );
384 }
385
386 #[test]
387 fn test_memory_store_overwrite() {
388 let store = MemorySessionStore::new();
389 let mut data = HashMap::new();
390 data.insert("key".to_string(), json!("old"));
391 store.write("session-1", data);
392
393 let mut new_data = HashMap::new();
394 new_data.insert("key".to_string(), json!("new"));
395 store.write("session-1", new_data);
396
397 assert_eq!(
398 store.read("session-1").unwrap().get("key"),
399 Some(&json!("new"))
400 );
401 }
402
403 fn make_session() -> Session {
409 Session::new("test-session-id", MemorySessionStore::new())
410 }
411
412 #[test]
413 fn test_session_set_get() {
414 let session = make_session();
415 session.set("user_id", json!(12345));
416 assert_eq!(session.get("user_id"), Some(json!(12345)));
417 }
418
419 #[test]
420 fn test_session_set_string_value() {
421 let session = make_session();
422 session.set("name", json!("alice"));
423 assert_eq!(session.get("name"), Some(json!("alice")));
424 }
425
426 #[test]
427 fn test_session_set_object_value() {
428 let session = make_session();
429 session.set("user", json!({"id": 1, "name": "bob"}));
430 let value = session.get("user").unwrap();
431 assert_eq!(value["id"], 1);
432 assert_eq!(value["name"], "bob");
433 }
434
435 #[test]
436 fn test_session_get_nonexistent_returns_none() {
437 let session = make_session();
438 assert_eq!(session.get("missing"), None);
439 }
440
441 #[test]
442 fn test_session_get_with_default_returns_value_when_exists() {
443 let session = make_session();
444 session.set("key", json!("actual"));
445 assert_eq!(
446 session.get_with_default("key", json!("default")),
447 json!("actual")
448 );
449 }
450
451 #[test]
452 fn test_session_get_with_default_returns_default_when_missing() {
453 let session = make_session();
454 assert_eq!(
455 session.get_with_default("missing", json!("default")),
456 json!("default")
457 );
458 }
459
460 #[test]
461 fn test_session_has_existing_key() {
462 let session = make_session();
463 session.set("key", json!(1));
464 assert!(session.has("key"));
465 }
466
467 #[test]
468 fn test_session_has_nonexistent_key() {
469 let session = make_session();
470 assert!(!session.has("missing"));
471 }
472
473 #[test]
474 fn test_session_delete_returns_value() {
475 let session = make_session();
476 session.set("key", json!("value"));
477 let removed = session.delete("key");
478 assert_eq!(removed, Some(json!("value")));
479 assert!(!session.has("key"));
480 }
481
482 #[test]
483 fn test_session_delete_nonexistent_returns_none() {
484 let session = make_session();
485 let removed = session.delete("missing");
486 assert_eq!(removed, None);
487 }
488
489 #[test]
490 fn test_session_clear_removes_all_data() {
491 let session = make_session();
492 session.set("key1", json!(1));
493 session.set("key2", json!(2));
494 session.set("key3", json!(3));
495
496 session.clear();
497
498 assert!(!session.has("key1"));
499 assert!(!session.has("key2"));
500 assert!(!session.has("key3"));
501 }
502
503 #[test]
504 fn test_session_all_returns_non_flash_data() {
505 let session = make_session();
506 session.set("key1", json!(1));
507 session.set("key2", json!("two"));
508 session.flash("temp", json!("flash"));
509
510 let all = session.all();
511 assert_eq!(all.len(), 2); assert_eq!(all.get("key1"), Some(&json!(1)));
513 assert_eq!(all.get("key2"), Some(&json!("two")));
514 }
515
516 #[test]
521 fn test_session_prefix_isolation() {
522 let store = MemorySessionStore::new();
523 let session_a = Session::new("sid", store.clone()).with_prefix("app_a_");
524 let session_b = Session::new("sid", store.clone()).with_prefix("app_b_");
525
526 session_a.set("user", json!("alice"));
527 session_b.set("user", json!("bob"));
528
529 assert_eq!(session_a.get("user"), Some(json!("alice")));
531 assert_eq!(session_b.get("user"), Some(json!("bob")));
532 }
533
534 #[test]
535 fn test_session_prefix_empty_by_default() {
536 let session = make_session();
537 assert_eq!(session.prefix, "");
538 }
539
540 #[test]
545 fn test_session_flash_set_get() {
546 let session = make_session();
547 session.flash("success", json!("操作成功"));
548 assert_eq!(session.get_flash("success"), Some(json!("操作成功")));
549 }
550
551 #[test]
552 fn test_session_flash_not_in_regular_get() {
553 let session = make_session();
554 session.flash("temp", json!("flash data"));
555
556 assert_eq!(session.get("temp"), None);
558 assert_eq!(session.get("__flash__:temp"), Some(json!("flash data")));
560 }
561
562 #[test]
563 fn test_session_clear_flash_removes_flash_data() {
564 let session = make_session();
565 session.flash("temp1", json!(1));
566 session.flash("temp2", json!(2));
567 session.set("regular", json!("keep"));
568
569 session.clear_flash();
570
571 assert_eq!(session.get_flash("temp1"), None);
573 assert_eq!(session.get_flash("temp2"), None);
574 assert_eq!(session.get("regular"), Some(json!("keep")));
576 }
577
578 #[test]
579 fn test_session_clear_flash_when_no_data() {
580 let session = make_session();
582 session.clear_flash();
583 }
584
585 #[test]
586 fn test_session_flush_equals_clear() {
587 let session1 = make_session();
588 let session2 = make_session();
589
590 session1.set("key", json!(1));
591 session2.set("key", json!(1));
592
593 session1.clear();
594 session2.flush();
595
596 assert!(!session1.has("key"));
598 assert!(!session2.has("key"));
599 }
600
601 #[test]
606 fn test_multiple_sessions_share_store() {
607 let store = Arc::new(MemorySessionStore::new());
608 let session1 = Session::with_shared_store("sid-1", store.clone());
609 let session2 = Session::with_shared_store("sid-2", store.clone());
610
611 session1.set("user", json!("alice"));
612 session2.set("user", json!("bob"));
613
614 assert_eq!(session1.get("user"), Some(json!("alice")));
616 assert_eq!(session2.get("user"), Some(json!("bob")));
617
618 session1.clear();
620 assert!(!session1.has("user"));
621 assert!(session2.has("user"));
622 }
623
624 #[test]
625 fn test_session_id_access() {
626 let session = Session::new("my-session-id", MemorySessionStore::new());
627 assert_eq!(session.session_id(), "my-session-id");
628 }
629
630 #[test]
635 fn test_php_consistency_session_full_flow() {
636 let store = Arc::new(MemorySessionStore::new());
638
639 let login_session = Session::with_shared_store("sid-login", store.clone());
641 login_session.set(
642 "szshop_clerk",
643 json!({"clerk_id": 100, "name": "张三", "store_id": 5}),
644 );
645
646 let later_session = Session::with_shared_store("sid-login", store.clone());
648 let clerk = later_session.get("szshop_clerk").unwrap();
649 assert_eq!(clerk["clerk_id"], 100);
650 assert_eq!(clerk["name"], "张三");
651 assert_eq!(clerk["store_id"], 5);
652
653 later_session.clear();
655 assert!(!later_session.has("szshop_clerk"));
656 }
657
658 #[test]
659 fn test_php_consistency_flash_message_flow() {
660 let store = Arc::new(MemorySessionStore::new());
662
663 let submit_session = Session::with_shared_store("sid", store.clone());
665 submit_session.flash("success", json!("保存成功"));
666
667 let redirect_session = Session::with_shared_store("sid", store.clone());
669 assert_eq!(
670 redirect_session.get_flash("success"),
671 Some(json!("保存成功"))
672 );
673
674 redirect_session.clear_flash();
676
677 let next_session = Session::with_shared_store("sid", store.clone());
679 assert_eq!(next_session.get_flash("success"), None);
680 }
681}