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
36#[derive(Debug, Clone)]
40pub struct TagEntry {
41 pub key: String,
42 pub value: Option<String>,
43}
44
45pub fn parse_tag(s: &str) -> Result<TagEntry, String> {
50 let (key, value) = parse_tag_string(s)?;
51 Ok(TagEntry { key, value })
52}
53
54pub fn tags_from_entries(entries: &[TagEntry]) -> Tags {
56 let mut tags = Tags::new();
57 for entry in entries {
58 match &entry.value {
59 Some(v) => tags.insert(&entry.key, v),
60 None => tags.add_label(&entry.key),
61 }
62 }
63 tags
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn test_parse_port_valid() {
72 assert_eq!(parse_port("1").unwrap(), 1);
73 assert_eq!(parse_port("3671").unwrap(), 3671);
74 assert_eq!(parse_port("65535").unwrap(), 65535);
75 }
76
77 #[test]
78 fn test_parse_port_zero_rejected() {
79 assert!(parse_port("0").is_err());
80 }
81
82 #[test]
83 fn test_parse_port_invalid_string() {
84 assert!(parse_port("abc").is_err());
85 assert!(parse_port("-1").is_err());
86 assert!(parse_port("99999").is_err());
87 }
88
89 #[test]
90 fn test_parse_nonzero_count_valid() {
91 assert_eq!(parse_nonzero_count("1").unwrap(), 1);
92 assert_eq!(parse_nonzero_count("50000").unwrap(), 50000);
93 }
94
95 #[test]
96 fn test_parse_nonzero_count_zero_rejected() {
97 assert!(parse_nonzero_count("0").is_err());
98 }
99
100 #[test]
101 fn test_parse_nonzero_count_invalid() {
102 assert!(parse_nonzero_count("abc").is_err());
103 assert!(parse_nonzero_count("-1").is_err());
104 }
105
106 #[test]
107 fn test_parse_tag_key_value() {
108 let entry = parse_tag("location=building-a").unwrap();
109 assert_eq!(entry.key, "location");
110 assert_eq!(entry.value, Some("building-a".to_string()));
111 }
112
113 #[test]
114 fn test_parse_tag_label() {
115 let entry = parse_tag("critical").unwrap();
116 assert_eq!(entry.key, "critical");
117 assert_eq!(entry.value, None);
118 }
119
120 #[test]
121 fn test_parse_tag_empty_value() {
122 let entry = parse_tag("key=").unwrap();
123 assert_eq!(entry.key, "key");
124 assert_eq!(entry.value, Some("".to_string()));
125 }
126
127 #[test]
128 fn test_parse_tag_invalid() {
129 assert!(parse_tag("").is_err());
130 assert!(parse_tag("=value").is_err());
131 }
132
133 #[test]
134 fn test_tags_from_entries() {
135 let entries = vec![
136 TagEntry { key: "location".to_string(), value: Some("building-a".to_string()) },
137 TagEntry { key: "floor".to_string(), value: Some("3".to_string()) },
138 TagEntry { key: "critical".to_string(), value: None },
139 ];
140
141 let tags = tags_from_entries(&entries);
142 assert_eq!(tags.get("location"), Some("building-a"));
143 assert_eq!(tags.get("floor"), Some("3"));
144 assert!(tags.has_label("critical"));
145 }
146}