1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::str::FromStr;
4use uuid::Uuid;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8#[serde(transparent)]
9pub struct SessionId(Uuid);
10
11impl SessionId {
12 pub fn new() -> Self {
13 Self(Uuid::new_v4())
14 }
15
16 pub fn from_uuid(uuid: Uuid) -> Self {
17 Self(uuid)
18 }
19
20 pub fn as_uuid(&self) -> &Uuid {
21 &self.0
22 }
23}
24
25impl Default for SessionId {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31impl fmt::Display for SessionId {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 self.0.fmt(f)
34 }
35}
36
37impl FromStr for SessionId {
38 type Err = uuid::Error;
39 fn from_str(s: &str) -> Result<Self, Self::Err> {
40 Uuid::from_str(s).map(Self)
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn generate_unique() {
50 let a = SessionId::new();
51 let b = SessionId::new();
52 assert_ne!(a, b);
53 }
54
55 #[test]
56 fn parse_roundtrip() {
57 let id = SessionId::new();
58 let s = id.to_string();
59 let parsed: SessionId = s.parse().unwrap();
60 assert_eq!(id, parsed);
61 }
62
63 #[test]
64 fn display_is_uuid_format() {
65 let id = SessionId::new();
66 let s = id.to_string();
67 assert_eq!(s.len(), 36);
68 assert_eq!(s.as_bytes()[8], b'-');
69 assert_eq!(s.as_bytes()[13], b'-');
70 assert_eq!(s.as_bytes()[18], b'-');
71 assert_eq!(s.as_bytes()[23], b'-');
72 }
73
74 #[test]
75 fn invalid_string_rejected() {
76 assert!("not-a-uuid".parse::<SessionId>().is_err());
77 }
78
79 #[test]
80 fn serde_roundtrip() {
81 let id = SessionId::new();
82 let json = serde_json::to_string(&id).unwrap();
83 let back: SessionId = serde_json::from_str(&json).unwrap();
84 assert_eq!(id, back);
85 }
86}