verse_session_id/
session_id.rs

1use crate::errors;
2use anyhow::Result;
3use std::cmp::Ordering;
4use std::fmt;
5
6/// Bytes of Session ID
7pub const SESSION_ID_SIZE: usize = 32;
8/// Session ID data
9pub type RawSessionId = [u8; SESSION_ID_SIZE];
10
11/// Session ID
12/// The session ID is the public key for ED25519.
13#[derive(Eq, PartialEq, PartialOrd, Ord, Hash, Clone, Copy)]
14pub struct SessionId(RawSessionId);
15
16fn compare_session_ids(a: &[u8], b: &[u8]) -> Ordering {
17    let n = a.len();
18    if n != b.len() {
19        // 想定外
20        return n.cmp(&b.len());
21        // return (n - b.len()) as i32;
22    }
23    for i in 0..n {
24        let diff = (a[i] as i32).cmp(&(b[i] as i32));
25        if diff.is_ne() {
26            return diff;
27        }
28    }
29    Ordering::Equal
30}
31
32impl SessionId {
33    pub fn eq_slice(&self, other: &impl AsRef<[u8]>) -> bool {
34        self.cmp_slice(other).is_eq()
35    }
36    pub fn cmp_slice(&self, other: impl AsRef<[u8]>) -> Ordering {
37        compare_session_ids(self.as_ref(), other.as_ref())
38    }
39    pub fn to_debug_string(&self) -> String {
40        let mut s = base64::encode(self);
41        s.truncate(7);
42        s
43    }
44    pub fn to_vec(&self) -> Vec<u8> {
45        self.0.to_vec()
46    }
47}
48impl Default for SessionId {
49    fn default() -> Self {
50        SessionId([0; SESSION_ID_SIZE])
51    }
52}
53impl fmt::Display for SessionId {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        write!(f, "{}", base64::encode(self.0))
56    }
57}
58impl std::str::FromStr for SessionId {
59    type Err = anyhow::Error;
60    fn from_str(s: &str) -> Result<Self, Self::Err> {
61        base64::decode(s)?.try_into()
62    }
63}
64
65impl TryFrom<&[u8]> for SessionId {
66    type Error = anyhow::Error;
67    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
68        let v: RawSessionId = value.try_into()?;
69        Ok(SessionId(v))
70    }
71}
72
73impl TryFrom<&Vec<u8>> for SessionId {
74    type Error = anyhow::Error;
75    fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
76        Self::try_from(value.as_ref() as &[u8])
77    }
78}
79impl TryFrom<Vec<u8>> for SessionId {
80    type Error = anyhow::Error;
81    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
82        Self::try_from(value.as_ref() as &[u8])
83    }
84}
85
86impl From<RawSessionId> for SessionId {
87    fn from(v: RawSessionId) -> Self {
88        SessionId(v)
89    }
90}
91impl From<SessionId> for Vec<u8> {
92    fn from(v: SessionId) -> Self {
93        v.to_vec()
94    }
95}
96
97impl AsRef<[u8]> for SessionId {
98    fn as_ref(&self) -> &[u8] {
99        &self.0
100    }
101}
102impl fmt::Debug for SessionId {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        write!(f, "{}", self.to_debug_string())
105    }
106}
107
108pub trait SessionIdCompatible {
109    fn to_bytes(&self) -> Option<&[u8]>;
110    fn to_session_id(&self) -> Result<SessionId> {
111        self.to_bytes().ok_or_else(errors::required!())?.try_into()
112    }
113    fn eq_slice(&self, other: &impl SessionIdCompatible) -> bool {
114        let a = self.to_bytes();
115        let b = other.to_bytes();
116        if a.is_none() && b.is_none() {
117            return true;
118        }
119        if a.is_none() != b.is_none() {
120            return false;
121        }
122        compare_session_ids(a.unwrap(), b.unwrap()).is_eq()
123    }
124    fn to_debug_string(&self) -> String {
125        match self.to_bytes() {
126            Some(v) => {
127                let mut s = base64::encode(v);
128                s.truncate(7);
129                s
130            }
131            None => "<NOID>".to_string(),
132        }
133    }
134}
135impl SessionIdCompatible for Option<Vec<u8>> {
136    fn to_bytes(&self) -> Option<&[u8]> {
137        self.as_ref().map(|v| v as &[u8])
138    }
139}
140impl<'a> SessionIdCompatible for Option<&'a SessionId> {
141    fn to_bytes(&self) -> Option<&[u8]> {
142        self.map(|v| v.as_ref())
143    }
144}
145impl<'a> SessionIdCompatible for &'a [u8] {
146    fn to_bytes(&self) -> Option<&[u8]> {
147        Some(self)
148    }
149}
150/* impl<'a> SessionIdCompatible for &'a Vec<u8> {
151    fn to_bytes(&self) -> Option<&[u8]> {
152        Some(self)
153    }
154} */
155impl SessionIdCompatible for Vec<u8> {
156    fn to_bytes(&self) -> Option<&[u8]> {
157        Some(self)
158    }
159}
160impl SessionIdCompatible for SessionId {
161    fn to_bytes(&self) -> Option<&[u8]> {
162        Some(self.as_ref())
163    }
164}
165/* impl<'a, T> SessionIdCompatible for T
166where
167    T: AsRef<[u8]>,
168{
169    fn to_bytes(&self) -> Option<&[u8]> {
170        Some(self)
171    }
172} */
173#[cfg(test)]
174mod tests {
175    use super::*;
176    use std::str::FromStr;
177
178    #[test]
179    fn test_session_id() {
180        let sid0 = SessionId::from([1; SESSION_ID_SIZE]);
181        let sid1 = SessionId::from([2; SESSION_ID_SIZE]);
182        assert_eq!(sid0, SessionId::from([1; SESSION_ID_SIZE]));
183        assert_ne!(sid0, sid1);
184        assert_eq!(sid0, sid0.clone());
185        assert!(sid0 < sid1);
186        assert!(sid0 != sid1);
187        assert!(sid0.cmp(&sid1).is_ne());
188        let sid00 = sid0;
189        assert!(sid00 == sid0);
190
191        let mut exists = std::collections::HashSet::<SessionId>::new();
192        assert!(!exists.contains(&sid0));
193        exists.insert(sid0.clone());
194        assert!(exists.contains(&sid0));
195        assert!(!exists.contains(&sid1));
196
197        assert!(sid0.cmp_slice(&sid1).is_ne());
198        assert!(!sid0.eq_slice(&sid1.to_vec()));
199
200        assert!(sid0.cmp_slice(&sid0).is_eq());
201        assert!(sid0.eq_slice(&sid0.to_vec()));
202
203        assert!(sid0.cmp_slice(&[]).is_ne());
204
205        assert_ne!(sid0.to_debug_string(), sid1.to_debug_string());
206        assert_eq!(sid0.to_debug_string(), format!("{:?}", sid0));
207
208        let str = format!("{}", sid0);
209        assert_eq!(SessionId::from_str(&str).unwrap(), sid0);
210
211        let str = format!("{:?}", sid0);
212        assert!(SessionId::from_str(&str).is_err());
213
214        let sid00 = SessionId::try_from(sid0.to_vec());
215        assert!(sid00.is_ok());
216        assert_eq!(sid00.unwrap(), sid0);
217
218        let sid00 = SessionId::try_from(&sid0.to_vec());
219        assert!(sid00.is_ok());
220        assert_eq!(sid00.unwrap(), sid0);
221
222        let v: Vec<u8> = sid0.into();
223        assert_eq!(v, sid0.to_vec());
224    }
225    #[test]
226    fn test_session_id_compatible() {
227        let sid0raw = [3; SESSION_ID_SIZE];
228        let sid0 = SessionId::from(sid0raw.clone());
229        let v0 = sid0.to_vec();
230        let v1 = SessionId::from([4; SESSION_ID_SIZE]).to_vec();
231        let none = None as Option<Vec<u8>>;
232
233        assert!(Some(vec![1u8]).to_session_id().is_err());
234        assert!(Some(v0.clone()).to_session_id().is_ok());
235        assert!(none.to_session_id().is_err());
236
237        assert_eq!(none.to_debug_string(), "<NOID>");
238
239        assert_eq!((&Some(v0.clone())).to_debug_string(), format!("{:?}", sid0));
240        assert_eq!(
241            (&Some(v0.clone())).to_debug_string(),
242            (Some(v0.clone())).to_debug_string(),
243        );
244        assert!(!(&Some(v0.clone())).eq_slice(&Some(v1.clone())));
245        assert!(!(&Some(v0.clone())).eq_slice(&none));
246        assert!(none.eq_slice(&none));
247        assert!(!(none).eq_slice(&Some(v0.clone())));
248
249        assert!((&Some(v0.clone())).eq_slice(&Some(&sid0)));
250        assert!((&Some(v0.clone())).eq_slice(&sid0));
251        let ar: &[u8] = &sid0raw[..];
252        assert!((&Some(v0.clone())).eq_slice(&ar));
253        assert!((&Some(v0.clone())).eq_slice(&v0));
254    }
255}