1use chrono::Utc;
2use rand::Rng;
3
4pub fn generate_convoy_id() -> String {
6 let date = Utc::now().format("%Y%m%d");
7 let hex: u16 = rand::thread_rng().gen();
8 format!("c-{}-{:04x}", date, hex)
9}
10
11pub fn generate_task_id() -> String {
13 let date = Utc::now().format("%Y%m%d");
14 let hex: u16 = rand::thread_rng().gen();
15 format!("t-{}-{:04x}", date, hex)
16}
17
18pub fn generate_session_id() -> String {
20 let date = Utc::now().format("%Y%m%d");
21 let hex: u16 = rand::thread_rng().gen();
22 format!("s-{}-{:04x}", date, hex)
23}
24
25pub fn parse_convoy_id(id: &str) -> Option<(&str, &str)> {
27 let parts: Vec<&str> = id.splitn(3, '-').collect();
28 if parts.len() == 3 && parts[0] == "c" && parts[1].len() == 8 && parts[2].len() == 4 {
29 Some((parts[1], parts[2]))
30 } else {
31 None
32 }
33}
34
35pub fn parse_task_id(id: &str) -> Option<(&str, &str)> {
37 let parts: Vec<&str> = id.splitn(3, '-').collect();
38 if parts.len() == 3 && parts[0] == "t" && parts[1].len() == 8 && parts[2].len() == 4 {
39 Some((parts[1], parts[2]))
40 } else {
41 None
42 }
43}
44
45pub fn parse_session_id(id: &str) -> Option<(&str, &str)> {
47 let parts: Vec<&str> = id.splitn(3, '-').collect();
48 if parts.len() == 3 && parts[0] == "s" && parts[1].len() == 8 && parts[2].len() == 4 {
49 Some((parts[1], parts[2]))
50 } else {
51 None
52 }
53}
54
55pub fn is_valid_convoy_id(id: &str) -> bool {
57 parse_convoy_id(id).is_some()
58}
59
60pub fn is_valid_task_id(id: &str) -> bool {
62 parse_task_id(id).is_some()
63}
64
65pub fn is_valid_session_id(id: &str) -> bool {
67 parse_session_id(id).is_some()
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn test_generate_convoy_id_format() {
76 let id = generate_convoy_id();
77 assert!(id.starts_with("c-"));
78 assert_eq!(id.len(), 15); assert!(is_valid_convoy_id(&id));
80 }
81
82 #[test]
83 fn test_generate_task_id_format() {
84 let id = generate_task_id();
85 assert!(id.starts_with("t-"));
86 assert_eq!(id.len(), 15); assert!(is_valid_task_id(&id));
88 }
89
90 #[test]
91 fn test_generate_session_id_format() {
92 let id = generate_session_id();
93 assert!(id.starts_with("s-"));
94 assert_eq!(id.len(), 15); assert!(is_valid_session_id(&id));
96 }
97
98 #[test]
99 fn test_parse_convoy_id() {
100 let (date, hex) = parse_convoy_id("c-20250116-a2f9").unwrap();
101 assert_eq!(date, "20250116");
102 assert_eq!(hex, "a2f9");
103 }
104
105 #[test]
106 fn test_parse_task_id() {
107 let (date, hex) = parse_task_id("t-20250116-3a2c").unwrap();
108 assert_eq!(date, "20250116");
109 assert_eq!(hex, "3a2c");
110 }
111
112 #[test]
113 fn test_invalid_ids() {
114 assert!(parse_convoy_id("invalid").is_none());
115 assert!(parse_convoy_id("t-20250116-a2f9").is_none()); assert!(parse_convoy_id("c-2025011-a2f9").is_none()); assert!(parse_convoy_id("c-20250116-a2f").is_none()); }
119
120 #[test]
121 fn test_uniqueness() {
122 let ids: Vec<String> = (0..100).map(|_| generate_convoy_id()).collect();
123 let unique: std::collections::HashSet<_> = ids.iter().collect();
124 assert!(unique.len() > 90);
126 }
127}