1use mabi_core::tags::{parse_tag_string, Tags};
7
8pub fn parse_port(s: &str) -> Result<u16, String> {
13 let port: u16 = s
14 .parse()
15 .map_err(|_| format!("'{s}' is not a valid port number"))?;
16 if port == 0 {
17 return Err("port must be between 1 and 65535 (port 0 is not allowed)".to_string());
18 }
19 Ok(port)
20}
21
22pub fn parse_nonzero_count(s: &str) -> Result<usize, String> {
27 let n: usize = s
28 .parse()
29 .map_err(|_| format!("'{s}' is not a valid number"))?;
30 if n == 0 {
31 return Err("value must be at least 1".to_string());
32 }
33 Ok(n)
34}
35
36pub fn parse_zero_or_more_count(s: &str) -> Result<usize, String> {
41 s.parse()
42 .map_err(|_| format!("'{s}' is not a valid number"))
43}
44
45#[derive(Debug, Clone)]
49pub struct TagEntry {
50 pub key: String,
51 pub value: Option<String>,
52}
53
54pub fn parse_tag(s: &str) -> Result<TagEntry, String> {
59 let (key, value) = parse_tag_string(s)?;
60 Ok(TagEntry { key, value })
61}
62
63pub fn tags_from_entries(entries: &[TagEntry]) -> Tags {
65 let mut tags = Tags::new();
66 for entry in entries {
67 match &entry.value {
68 Some(v) => tags.insert(&entry.key, v),
69 None => tags.add_label(&entry.key),
70 }
71 }
72 tags
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn test_parse_port_valid() {
81 assert_eq!(parse_port("1").unwrap(), 1);
82 assert_eq!(parse_port("3671").unwrap(), 3671);
83 assert_eq!(parse_port("65535").unwrap(), 65535);
84 }
85
86 #[test]
87 fn test_parse_port_zero_rejected() {
88 assert!(parse_port("0").is_err());
89 }
90
91 #[test]
92 fn test_parse_port_invalid_string() {
93 assert!(parse_port("abc").is_err());
94 assert!(parse_port("-1").is_err());
95 assert!(parse_port("99999").is_err());
96 }
97
98 #[test]
99 fn test_parse_nonzero_count_valid() {
100 assert_eq!(parse_nonzero_count("1").unwrap(), 1);
101 assert_eq!(parse_nonzero_count("50000").unwrap(), 50000);
102 }
103
104 #[test]
105 fn test_parse_nonzero_count_zero_rejected() {
106 assert!(parse_nonzero_count("0").is_err());
107 }
108
109 #[test]
110 fn test_parse_nonzero_count_invalid() {
111 assert!(parse_nonzero_count("abc").is_err());
112 assert!(parse_nonzero_count("-1").is_err());
113 }
114
115 #[test]
116 fn test_parse_zero_or_more_count_valid() {
117 assert_eq!(parse_zero_or_more_count("0").unwrap(), 0);
118 assert_eq!(parse_zero_or_more_count("100").unwrap(), 100);
119 }
120
121 #[test]
122 fn test_parse_zero_or_more_count_invalid() {
123 assert!(parse_zero_or_more_count("abc").is_err());
124 assert!(parse_zero_or_more_count("-1").is_err());
125 }
126
127 #[test]
128 fn test_parse_tag_key_value() {
129 let entry = parse_tag("location=building-a").unwrap();
130 assert_eq!(entry.key, "location");
131 assert_eq!(entry.value, Some("building-a".to_string()));
132 }
133
134 #[test]
135 fn test_parse_tag_label() {
136 let entry = parse_tag("critical").unwrap();
137 assert_eq!(entry.key, "critical");
138 assert_eq!(entry.value, None);
139 }
140
141 #[test]
142 fn test_parse_tag_empty_value() {
143 let entry = parse_tag("key=").unwrap();
144 assert_eq!(entry.key, "key");
145 assert_eq!(entry.value, Some("".to_string()));
146 }
147
148 #[test]
149 fn test_parse_tag_invalid() {
150 assert!(parse_tag("").is_err());
151 assert!(parse_tag("=value").is_err());
152 }
153
154 #[test]
155 fn test_tags_from_entries() {
156 let entries = vec![
157 TagEntry {
158 key: "location".to_string(),
159 value: Some("building-a".to_string()),
160 },
161 TagEntry {
162 key: "floor".to_string(),
163 value: Some("3".to_string()),
164 },
165 TagEntry {
166 key: "critical".to_string(),
167 value: None,
168 },
169 ];
170
171 let tags = tags_from_entries(&entries);
172 assert_eq!(tags.get("location"), Some("building-a"));
173 assert_eq!(tags.get("floor"), Some("3"));
174 assert!(tags.has_label("critical"));
175 }
176}