dce_session/
session.rs

1use std::collections::HashMap;
2use std::time::{SystemTime, UNIX_EPOCH};
3use rand::random;
4use sha2::{Digest, Sha256};
5use serde::{Deserialize, Serialize};
6use dce_util::mixed::{DceErr, DceResult};
7#[cfg(feature = "async")]
8use async_trait::async_trait;
9
10pub const DEFAULT_ID_NAME: &str = "dcesid";
11pub const DEFAULT_TTL_MINUTES: u16 = 60;
12
13#[derive(Clone)]
14pub struct Meta {
15    sid_name: &'static str,
16    ttl_minutes: u16,
17    create_stamp: u64,
18    sid: String,
19    touches: Option<bool>,
20    #[cfg(feature = "test")]
21    sid_pool: Vec<String>,
22}
23
24impl Meta {
25    pub fn sid_name(&self) -> &str {
26        self.sid_name
27    }
28
29    pub fn ttl_minutes(&self) -> u16 {
30        self.ttl_minutes
31    }
32
33    pub fn ttl_seconds(&self) -> u32 {
34        self.ttl_minutes as u32 * 60
35    }
36
37    pub fn create_stamp(&self) -> u64 {
38        self.create_stamp
39    }
40
41    pub fn sid(&self) -> &str {
42        self.sid.as_str()
43    }
44
45    pub fn config(&mut self, sid_name: Option<&'static str>, ttl_minutes: Option<u16>) {
46        if let Some(sid_name) = sid_name { self.sid_name = sid_name; }
47        if let Some(ttl_minutes) = ttl_minutes { self.ttl_minutes = ttl_minutes; }
48    }
49
50    pub fn renew(&mut self, sid: Option<String>) -> DceResult<()> {
51        self.touches = None;
52        Ok(if let Some(sid) = sid {
53            (self.ttl_minutes, self.create_stamp) = Self::parse_sid(sid.as_str())?;
54            self.sid = sid;
55        } else {
56            (self.sid, self.create_stamp) = Self::generate_id(self.ttl_minutes, #[cfg(feature = "test")] &mut self.sid_pool)?;
57        })
58    }
59
60    pub fn new(ttl_minutes: u16) -> DceResult<Self> {
61        #[cfg(feature = "test")] let mut sid_pool = vec![];
62        let (sid, create_stamp) = Self::generate_id(ttl_minutes, #[cfg(feature = "test")] &mut sid_pool)?;
63        Ok(Self {ttl_minutes, create_stamp, sid, sid_name: DEFAULT_ID_NAME, touches: None, #[cfg(feature = "test")] sid_pool })
64    }
65
66    pub fn new_with_sid(mut sid_pool: Vec<String>) -> DceResult<Self> {
67        assert!(! sid_pool.is_empty());
68        let sid = sid_pool.remove(0);
69        let (ttl_minutes, create_stamp) = Self::parse_sid(&sid)?;
70        Ok(Self {ttl_minutes, create_stamp, sid, sid_name: DEFAULT_ID_NAME, touches: None, #[cfg(feature = "test")] sid_pool })
71    }
72
73    fn parse_sid(sid: &str) -> DceResult<(u16, u64)> {
74        const MIN_SID_LEN: usize = 76;
75        if sid.len() < MIN_SID_LEN { return DceErr::closed0_wrap(format!(r#"invalid sid "{}", less then {} chars"#, sid, MIN_SID_LEN)); }
76        let ttl_minutes = u16::from_str_radix(&sid[64..68], 16).map_err(DceErr::closed0)?;
77        let create_stamp = u64::from_str_radix(&sid[68..], 16).map_err(DceErr::closed0)?;
78        Ok((ttl_minutes, create_stamp))
79    }
80
81    #[allow(unused)]
82    fn generate_id(ttl_minutes: u16, #[cfg(feature = "test")] sid_pool: &mut Vec<String>) -> DceResult<(String, u64)> {
83        #[cfg(not(feature = "test"))] return Self::gen_id(ttl_minutes);
84        #[cfg(feature = "test")] return {
85            let sid = sid_pool.remove(0);
86            Self::parse_sid(&sid).map(|(_, create_stamp)| (sid, create_stamp))
87        };
88    }
89
90    pub fn gen_id(ttl_minutes: u16) -> DceResult<(String, u64)> {
91        let now = SystemTime::now().duration_since(UNIX_EPOCH).map_err(DceErr::closed0)?;
92        let now_secs = now.as_secs();
93        let mut hasher = Sha256::new();
94        hasher.update(format!("{}-{}", now.as_nanos(), random::<usize>()).as_bytes());
95        Ok((format!("{:X}{:04X}{:X}", hasher.finalize(), ttl_minutes, now_secs), now_secs))
96    }
97}
98
99
100macro_rules! auto_async {
101    { $($(#[$($meta:meta),+])+, $($async: ident)?);+ } => {
102        #[cfg_attr(feature = "async", async_trait)]
103        pub trait Session: Sized {
104            fn new(ttl_minutes: u16) -> DceResult<Self>;
105            
106            fn new_with_id(sid_pool: Vec<String>) -> DceResult<Self>;
107        
108            fn meta(&self) -> &Meta;
109        
110            fn meta_mut(&mut self) -> &mut Meta;
111            
112            fn id(&self) -> &str {
113                self.meta().sid()
114            }
115        
116            fn key(&self) -> String {
117                Self::gen_key(self.meta().sid_name, self.id())
118            }
119        
120            fn gen_key(sid_prefix: &str, id: &str) -> String;
121        
122            $($(#[$($meta),+])+
123            $($async)? fn silent_set(&mut self, field: &str, value: &str) -> DceResult<bool>; )+
124        
125            $($(#[$($meta),+])+
126            $($async)? fn silent_get(&mut self, field: &str) -> DceResult<String>; )+
127        
128            $($(#[$($meta),+])+
129            $($async)? fn silent_del(&mut self, field: &str) -> DceResult<bool>; )+
130        
131            $($(#[$($meta),+])+
132            $($async)? fn destroy(&mut self) -> DceResult<bool>; )+
133        
134            $($(#[$($meta),+])+
135            $($async)? fn touch(&mut self) -> DceResult<bool>; )+
136        
137            $($(#[$($meta),+])+
138            $($async)? fn load(&mut self, data: HashMap<String, String>) -> DceResult<bool>; )+
139        
140            $($(#[$($meta),+])+
141            $($async)? fn raw(&mut self) -> DceResult<HashMap<String, String>>; )+
142        
143            $($(#[$($meta),+])+
144            $($async)? fn ttl_passed(&mut self) -> DceResult<u32>; )+
145        
146            $($(#[$($meta),+])+
147            $($async)? fn set<T: Serialize + Sync>(&mut self, field: &str, value: &T) -> DceResult<bool> {
148                let value = serde_json::to_string::<T>(value).map_err(DceErr::closed0)?;
149                #[cfg(feature = "async")]
150                match self.silent_set(field, &value).await {
151                    Ok(res) if res => self.try_touch().await,
152                    result => result,
153                }
154                #[cfg(not(feature = "async"))]
155                match self.silent_set(field, &value) {
156                    Ok(res) if res => self.try_touch(),
157                    result => result,
158                }
159            } )+
160        
161            $($(#[$($meta),+])+
162            $($async)? fn get<T: for<'a> Deserialize<'a> + Send>(&mut self, field: &str) -> DceResult<T> {
163                #[cfg(feature = "async")]
164                let value = self.silent_get(field).await;
165                #[cfg(not(feature = "async"))]
166                let value = self.silent_get(field);
167                if value.is_ok() {
168                    #[cfg(feature = "async")]
169                    self.try_touch().await?;
170                    #[cfg(not(feature = "async"))]
171                    self.try_touch()?;
172                }
173                serde_json::from_str(&value?).map_err(DceErr::closed0)
174            } )+
175        
176            $($(#[$($meta),+])+
177            $($async)? fn del(&mut self, field: &str) -> DceResult<bool> {
178                #[cfg(feature = "async")]
179                match self.silent_del(field).await {
180                    Ok(res) if res => self.try_touch().await,
181                    result => result,
182                }
183                #[cfg(not(feature = "async"))]
184                match self.silent_del(field) {
185                    Ok(res) if res => self.try_touch(),
186                    result => result,
187                }
188            } )+
189        
190            $($(#[$($meta),+])+
191            $($async)? fn try_touch(&mut self) -> DceResult<bool> {
192                if self.meta().touches == None {
193                    #[cfg(feature = "async")]
194                    let touches = self.touch().await?;
195                    #[cfg(not(feature = "async"))]
196                    let touches = self.touch()?;
197                    self.meta_mut().touches = Some(touches);
198                }
199                Ok(matches!(self.meta().touches, Some(true)))
200            } )+
201            
202            $($(#[$($meta),+])+
203            $($async)? fn renew(&mut self, filters: HashMap<String, Option<String>>) -> DceResult<bool> where Self: Send {
204                #[cfg(feature = "async")]
205                return renew(self, filters).await;
206                #[cfg(not(feature = "async"))]
207                return renew(self, filters);
208            } )+
209        
210            fn clone_with_id(&mut self, id: String) -> DceResult<Self> where Self: Clone {
211                clone_with_id(self, id)
212            }
213
214            fn cloned_mut(&mut self) -> &mut Option<Box<Self>>;
215        
216            $($(#[$($meta),+])+
217            $($async)? fn cloned_silent_set(&mut self, field: &str, value: &str) -> DceResult<bool>; )+
218        
219            $($(#[$($meta),+])+
220            $($async)? fn cloned_destroy(&mut self) -> DceResult<bool>; )+
221        
222            $($(#[$($meta),+])+
223            $($async)? fn cloned_touch(&mut self) -> DceResult<bool>; )+
224        
225            $($(#[$($meta),+])+
226            $($async)? fn cloned_ttl_passed(&mut self) -> DceResult<u32>; )+
227        }
228        
229        pub fn clone_with_id<S: Session + Clone>(session: &mut S, id: String) -> DceResult<S> {
230            let mut cloned = session.clone();
231            cloned.meta_mut().renew(Some(id))?;
232            Ok(cloned)
233        }
234        
235        $($(#[$($meta),+])+
236        pub $($async)? fn renew<S: Session + Send>(session: &mut S, filters: HashMap<String, Option<String>>) -> DceResult<bool> {
237            #[cfg(feature = "async")]
238            let mut raw = session.raw().await?;
239            #[cfg(not(feature = "async"))]
240            let mut raw = session.raw()?;
241            for (k, v) in filters {
242                if let Some(v) = v {
243                    let _ = raw.insert(k, v);
244                } else {
245                    let _ = raw.remove(&k);
246                }
247            }
248            session.meta_mut().renew(None)?;
249            if raw.is_empty() {
250                return Ok(true);
251            }
252            #[cfg(feature = "async")]
253            session.load(raw).await?;
254            #[cfg(not(feature = "async"))]
255            session.load(raw)?;
256            #[cfg(feature = "async")]
257            return session.try_touch().await;
258            #[cfg(not(feature = "async"))]
259            return session.try_touch();
260        } )+
261    };
262}
263
264auto_async!{ #[cfg(feature = "async")], async; #[cfg(not(feature = "async"))], }