ao_core/
opencode_session_id.rs1pub fn as_valid_opencode_session_id(value: impl AsRef<str>) -> Option<String> {
2 let s = value.as_ref().trim();
3 if s.is_empty() {
4 return None;
5 }
6 if !s.starts_with("ses_") {
7 return None;
8 }
9 let tail: Vec<u8> = s.bytes().skip(4).collect();
11 if !tail.is_empty()
12 && tail
13 .iter()
14 .all(|&b| matches!(b, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' | b'-'))
15 {
16 Some(s.to_string())
17 } else {
18 None
19 }
20}
21
22#[cfg(test)]
23mod tests {
24 use super::*;
25
26 #[test]
27 fn valid_ids_are_accepted() {
28 assert_eq!(
29 as_valid_opencode_session_id("ses_abc123"),
30 Some("ses_abc123".to_string())
31 );
32 assert_eq!(
33 as_valid_opencode_session_id("ses_A-B_C"),
34 Some("ses_A-B_C".to_string())
35 );
36 }
37
38 #[test]
39 fn prefix_only_is_rejected() {
40 assert_eq!(as_valid_opencode_session_id("ses_"), None);
42 }
43
44 #[test]
45 fn empty_and_no_prefix_rejected() {
46 assert_eq!(as_valid_opencode_session_id(""), None);
47 assert_eq!(as_valid_opencode_session_id("abc123"), None);
48 }
49
50 #[test]
51 fn invalid_chars_rejected() {
52 assert_eq!(as_valid_opencode_session_id("ses_ab!c"), None);
53 assert_eq!(as_valid_opencode_session_id("ses_ x"), None);
54 }
55
56 #[test]
57 fn whitespace_is_trimmed() {
58 assert_eq!(
59 as_valid_opencode_session_id(" ses_ok "),
60 Some("ses_ok".to_string())
61 );
62 }
63}