1use std::str::FromStr;
4
5use nom::{
6 character::complete::{char, digit1},
7 combinator::all_consuming,
8 sequence::preceded,
9 IResult, Parser,
10};
11use serde::{Deserialize, Serialize};
12
13use crate::error::{map_add_intent, Error};
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19pub struct SessionId(String);
20
21impl FromStr for SessionId {
22 type Err = Error;
23
24 fn from_str(input: &str) -> std::result::Result<Self, Self::Err> {
27 let desc = "SessionId";
28 let intent = "##{session_id}";
29
30 let (_, sess_id) = all_consuming(parse::session_id)
31 .parse(input)
32 .map_err(|e| map_add_intent(desc, intent, e))?;
33
34 Ok(sess_id)
35 }
36}
37
38impl SessionId {
39 pub fn as_str(&self) -> &str {
40 &self.0
41 }
42}
43
44pub(crate) mod parse {
45 use super::*;
46
47 pub fn session_id(input: &str) -> IResult<&str, SessionId> {
48 let (input, digit) = preceded(char('$'), digit1).parse(input)?;
49 let id = format!("${digit}");
50 Ok((input, SessionId(id)))
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn test_parse_session_id_fn() {
60 let actual = parse::session_id("$43");
61 let expected = Ok(("", SessionId("$43".into())));
62 assert_eq!(actual, expected);
63
64 let actual = parse::session_id("$4");
65 let expected = Ok(("", SessionId("$4".into())));
66 assert_eq!(actual, expected);
67 }
68
69 #[test]
70 fn test_parse_session_id_struct() {
71 let actual = SessionId::from_str("$43");
72 assert!(actual.is_ok());
73 assert_eq!(actual.unwrap(), SessionId("$43".into()));
74
75 let actual = SessionId::from_str("4:38");
76 assert!(matches!(
77 actual,
78 Err(Error::ParseError {
79 desc: "SessionId",
80 intent: "##{session_id}",
81 err: _
82 })
83 ));
84 }
85
86 #[test]
87 fn test_parse_session_id_with_large_number() {
88 let session_id = SessionId::from_str("$99999").unwrap();
89 assert_eq!(session_id, SessionId("$99999".into()));
90 }
91
92 #[test]
93 fn test_parse_session_id_zero() {
94 let session_id = SessionId::from_str("$0").unwrap();
95 assert_eq!(session_id, SessionId("$0".into()));
96 }
97
98 #[test]
99 fn test_parse_session_id_fails_on_wrong_prefix() {
100 assert!(SessionId::from_str("@1").is_err());
102 assert!(SessionId::from_str("%1").is_err());
103 }
104
105 #[test]
106 fn test_parse_session_id_fails_on_no_prefix() {
107 assert!(SessionId::from_str("123").is_err());
108 }
109
110 #[test]
111 fn test_parse_session_id_fails_on_empty() {
112 assert!(SessionId::from_str("").is_err());
113 assert!(SessionId::from_str("$").is_err());
114 }
115
116 #[test]
117 fn test_parse_session_id_fails_on_non_numeric() {
118 assert!(SessionId::from_str("$abc").is_err());
119 assert!(SessionId::from_str("$12abc").is_err());
120 }
121
122 #[test]
123 fn test_parse_session_id_fails_on_extra_content() {
124 assert!(SessionId::from_str("$12:extra").is_err());
126 }
127
128 #[test]
129 fn test_session_id_leaves_remaining_in_parser() {
130 let (remaining, session_id) = parse::session_id("$42:rest").unwrap();
132 assert_eq!(remaining, ":rest");
133 assert_eq!(session_id, SessionId("$42".into()));
134 }
135}