verse_session_id/
session_id.rs1use crate::errors;
2use anyhow::Result;
3use std::cmp::Ordering;
4use std::fmt;
5
6pub const SESSION_ID_SIZE: usize = 32;
8pub type RawSessionId = [u8; SESSION_ID_SIZE];
10
11#[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 return n.cmp(&b.len());
21 }
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}
150impl 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#[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}