data_modelling_sdk/models/
tag.rs1use std::fmt;
9use std::str::FromStr;
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub enum Tag {
14 Simple(String),
16 Pair(String, String),
18 List(String, Vec<String>),
20}
21
22impl FromStr for Tag {
23 type Err = ();
24
25 fn from_str(s: &str) -> Result<Self, Self::Err> {
35 let s = s.trim();
36
37 if let Some(colon_pos) = s.find(':') {
39 let key = s[..colon_pos].trim().to_string();
40 let value_part = s[colon_pos + 1..].trim();
41
42 if value_part.starts_with('[') && value_part.ends_with(']') {
44 let values_str = &value_part[1..value_part.len() - 1];
46 let values: Vec<String> = values_str
47 .split(',')
48 .map(|v| v.trim().to_string())
49 .filter(|v| !v.is_empty())
50 .collect();
51
52 if !key.is_empty() && !values.is_empty() {
53 return Ok(Tag::List(key, values));
54 }
55 } else {
56 if value_part.contains(':') {
58 return Ok(Tag::Simple(s.to_string()));
60 }
61
62 let value = value_part.to_string();
64 if !key.is_empty() && !value.is_empty() {
65 return Ok(Tag::Pair(key, value));
66 }
67 }
68 }
69
70 if !s.is_empty() {
72 Ok(Tag::Simple(s.to_string()))
73 } else {
74 Err(())
75 }
76 }
77}
78
79impl fmt::Display for Tag {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 match self {
88 Tag::Simple(s) => write!(f, "{}", s),
89 Tag::Pair(key, value) => write!(f, "{}:{}", key, value),
90 Tag::List(key, values) => {
91 let values_str = values.join(", ");
92 write!(f, "{}:[{}]", key, values_str)
93 }
94 }
95 }
96}
97
98impl serde::Serialize for Tag {
99 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
100 where
101 S: serde::Serializer,
102 {
103 serializer.serialize_str(&self.to_string())
104 }
105}
106
107impl<'de> serde::Deserialize<'de> for Tag {
108 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
109 where
110 D: serde::Deserializer<'de>,
111 {
112 let s = String::deserialize(deserializer)?;
113 Tag::from_str(&s).map_err(|_| serde::de::Error::custom("Invalid tag format"))
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn test_simple_tag_parsing() {
123 let tag = Tag::from_str("finance").unwrap();
124 assert_eq!(tag, Tag::Simple("finance".to_string()));
125 assert_eq!(tag.to_string(), "finance");
126 }
127
128 #[test]
129 fn test_pair_tag_parsing() {
130 let tag = Tag::from_str("Environment:Dev").unwrap();
131 assert_eq!(tag, Tag::Pair("Environment".to_string(), "Dev".to_string()));
132 assert_eq!(tag.to_string(), "Environment:Dev");
133 }
134
135 #[test]
136 fn test_list_tag_parsing() {
137 let tag = Tag::from_str("SecondaryDomains:[XXXXX, PPPP]").unwrap();
138 assert_eq!(
139 tag,
140 Tag::List(
141 "SecondaryDomains".to_string(),
142 vec!["XXXXX".to_string(), "PPPP".to_string()]
143 )
144 );
145 assert_eq!(tag.to_string(), "SecondaryDomains:[XXXXX, PPPP]");
146 }
147
148 #[test]
149 fn test_list_tag_with_spaces() {
150 let tag = Tag::from_str("SecondaryDomains:[XXXXX, PPPP , QQQQ]").unwrap();
151 assert_eq!(
152 tag,
153 Tag::List(
154 "SecondaryDomains".to_string(),
155 vec!["XXXXX".to_string(), "PPPP".to_string(), "QQQQ".to_string()]
156 )
157 );
158 }
159
160 #[test]
161 fn test_malformed_tag_fallback() {
162 let tag = Tag::from_str("Key:Value1:Value2").unwrap();
164 assert_eq!(tag, Tag::Simple("Key:Value1:Value2".to_string()));
165 }
166
167 #[test]
168 fn test_empty_tag_error() {
169 assert!(Tag::from_str("").is_err());
170 assert!(Tag::from_str(" ").is_err());
171 }
172
173 #[test]
174 fn test_tag_serialization() {
175 let simple = Tag::Simple("finance".to_string());
176 let pair = Tag::Pair("Environment".to_string(), "Dev".to_string());
177 let list = Tag::List(
178 "SecondaryDomains".to_string(),
179 vec!["XXXXX".to_string(), "PPPP".to_string()],
180 );
181
182 assert_eq!(simple.to_string(), "finance");
183 assert_eq!(pair.to_string(), "Environment:Dev");
184 assert_eq!(list.to_string(), "SecondaryDomains:[XXXXX, PPPP]");
185 }
186}