1use std::collections::HashMap;
4use std::sync::{OnceLock, RwLock};
5use uuid::Uuid;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct SessionRecord {
9 pub backend_id: String,
10 pub protocol_session_id: String,
11}
12
13static REGISTRY: OnceLock<RwLock<HashMap<String, SessionRecord>>> = OnceLock::new();
14
15fn registry() -> &'static RwLock<HashMap<String, SessionRecord>> {
16 REGISTRY.get_or_init(|| RwLock::new(HashMap::new()))
17}
18
19pub fn register_session(backend_id: &str, protocol_session_id: &str) -> String {
21 let session_id = format!("luft-{}", Uuid::now_v7());
22 registry().write().expect("session registry poisoned").insert(
23 session_id.clone(),
24 SessionRecord {
25 backend_id: backend_id.to_string(),
26 protocol_session_id: protocol_session_id.to_string(),
27 },
28 );
29 session_id
30}
31
32pub fn restore_session(
34 session_id: &str,
35 backend_id: &str,
36 protocol_session_id: &str,
37) {
38 registry().write().expect("session registry poisoned").insert(
39 session_id.to_string(),
40 SessionRecord {
41 backend_id: backend_id.to_string(),
42 protocol_session_id: protocol_session_id.to_string(),
43 },
44 );
45}
46
47pub fn resolve_session(session_id: &str, backend_id: &str) -> Option<SessionRecord> {
49 registry()
50 .read()
51 .expect("session registry poisoned")
52 .get(session_id)
53 .filter(|record| record.backend_id == backend_id)
54 .cloned()
55}
56
57pub fn remove_session(session_id: &str) {
59 registry()
60 .write()
61 .expect("session registry poisoned")
62 .remove(session_id);
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn register_and_resolve_opaque_session() {
71 let id = register_session("acp", "protocol-1");
72 assert!(id.starts_with("luft-"));
73 assert_eq!(
74 resolve_session(&id, "acp").unwrap().protocol_session_id,
75 "protocol-1"
76 );
77 assert!(resolve_session(&id, "other").is_none());
78 remove_session(&id);
79 assert!(resolve_session(&id, "acp").is_none());
80 }
81}