Skip to main content

senax_common/session/
session_key.rs

1use rand::{Rng as _, distr::Alphanumeric};
2use std::convert::TryFrom;
3use std::time::{SystemTime, UNIX_EPOCH};
4
5const KEY_LENGTH: usize = 64;
6const FULL_KEY_LENGTH: usize = 80;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct SessionKey(String);
10
11impl TryFrom<String> for SessionKey {
12    type Error = anyhow::Error;
13
14    fn try_from(val: String) -> Result<Self, Self::Error> {
15        anyhow::ensure!(
16            val.len() == FULL_KEY_LENGTH,
17            "Session ID length is invalid."
18        );
19        Ok(SessionKey(val))
20    }
21}
22
23impl From<SessionKey> for String {
24    fn from(key: SessionKey) -> Self {
25        key.0
26    }
27}
28
29impl From<&SessionKey> for String {
30    fn from(key: &SessionKey) -> Self {
31        key.0.clone()
32    }
33}
34
35impl Default for SessionKey {
36    fn default() -> Self {
37        Self::new()
38    }
39}
40
41impl std::fmt::Display for SessionKey {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        write!(f, "{}", self.0)
44    }
45}
46
47impl SessionKey {
48    pub fn new() -> SessionKey {
49        Self::generate(SystemTime::now())
50    }
51    pub fn generate_past(ttl: std::time::Duration) -> SessionKey {
52        Self::generate(SystemTime::now().checked_sub(ttl).unwrap())
53    }
54    fn generate(time: SystemTime) -> SessionKey {
55        let mut rng = rand::rng();
56        let value = std::iter::repeat(())
57            .map(|()| rng.sample(Alphanumeric))
58            .take(KEY_LENGTH)
59            .collect::<Vec<_>>();
60        let time = (time.duration_since(UNIX_EPOCH).unwrap().as_nanos() >> 2) as u64;
61        SessionKey(format!(
62            "{:016X}{}",
63            time,
64            String::from_utf8(value).unwrap()
65        ))
66    }
67    pub fn hash(&self) -> u32 {
68        crc32fast::hash(self.0.as_bytes())
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test() {
78        let key = SessionKey::new();
79        let str: String = key.into();
80        assert_eq!(str.len(), FULL_KEY_LENGTH);
81    }
82}