1struct FieldKeyVisitor;
2
3impl<'de> serde::de::Visitor<'de> for FieldKeyVisitor {
4 type Value = FieldKey;
5
6 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
7 formatter.write_str("a valid field key")
8 }
9
10 fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
11 FieldKey::new(value).map_err(|err| E::custom(format!("{}", err)))
12 }
13}
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
26pub struct FieldKey {
27 key: String,
28}
29
30impl serde::Serialize for FieldKey {
31 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
32 serializer.serialize_str(&self.key)
33 }
34}
35
36impl<'de> serde::Deserialize<'de> for FieldKey {
37 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
38 where
39 D: serde::Deserializer<'de>,
40 {
41 deserializer.deserialize_str(FieldKeyVisitor)
42 }
43}
44
45impl std::fmt::Display for FieldKey {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 write!(f, "{}", self.key)
48 }
49}
50
51impl FieldKey {
52 pub fn new(key: &str) -> Result<Self, crate::Error> {
54 let key = key.to_lowercase();
55 let mut parsed_key = String::new();
56
57 if !Self::validate(&key) {
58 return Err(crate::Error::new("Invalid field key"));
59 }
60
61 Self::parse(&key, &mut parsed_key)?;
62 Ok(Self { key: parsed_key })
63 }
64
65 pub fn as_str(&self) -> &str {
67 &self.key
68 }
69
70 fn parse(text: &str, writer: &mut impl std::fmt::Write) -> Result<(), crate::Error> {
71 let split_index = match text.find('.') {
72 Some(index) => index,
73 None => {
74 writer.write_str(text)?;
75
76 return Ok(());
77 }
78 };
79 let (before, after) = text.split_at(split_index);
80 writer.write_str(before)?;
81
82 let after = &after[1..];
83
84 if !after.is_empty() {
85 writer.write_char('.')?;
86 Self::parse(after, writer)?;
87 }
88
89 Ok(())
90 }
91
92 pub(crate) fn validate(text: &str) -> bool {
93 if text.is_empty() {
94 return false;
95 }
96
97 let split_index = match text.find('.') {
98 Some(index) => index,
99 None => {
100 if !Self::validate_part(text) {
101 return false;
102 }
103
104 return true;
105 }
106 };
107 let (before, after) = text.split_at(split_index);
108
109 if !Self::validate_part(before) {
110 return false;
111 }
112
113 let after = &after[1..];
114
115 if !Self::validate(after) {
116 return false;
117 }
118
119 true
120 }
121
122 fn validate_part(text: &str) -> bool {
123 if text.is_empty() {
124 return false;
125 }
126
127 let first_char = text.chars().next().unwrap();
128
129 if !(first_char.is_ascii_alphabetic() || first_char == '_') {
130 return false;
131 }
132
133 for character in text.chars().skip(1) {
134 if !(character.is_ascii_alphanumeric() || character == '_') {
135 return false;
136 }
137 }
138
139 true
140 }
141}
142
143impl TryFrom<&str> for FieldKey {
144 type Error = crate::Error;
145
146 fn try_from(value: &str) -> Result<Self, Self::Error> {
147 Self::new(value)
148 }
149}
150
151impl TryFrom<&String> for FieldKey {
152 type Error = crate::Error;
153
154 fn try_from(value: &String) -> Result<Self, Self::Error> {
155 Self::new(value)
156 }
157}
158
159impl TryFrom<String> for FieldKey {
160 type Error = crate::Error;
161
162 fn try_from(value: String) -> Result<Self, Self::Error> {
163 Self::new(&value)
164 }
165}
166
167impl TryFrom<&FieldKey> for FieldKey {
168 type Error = crate::Error;
169
170 fn try_from(value: &FieldKey) -> Result<Self, Self::Error> {
171 Ok(value.to_owned())
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[rstest::rstest]
180 #[case("test", "test")]
181 #[case("Test", "test")]
182 #[case("_test", "_test")]
183 #[case("test_", "test_")]
184 #[case("_1test", "_1test")]
185 #[case("abc.def", "abc.def")]
186 #[case("abc.def.ghi", "abc.def.ghi")]
187 #[case("abc123.def456", "abc123.def456")]
188 #[case("_abc._def", "_abc._def")]
189 fn test_field_key_new_success(#[case] input: &str, #[case] expected: &str) {
190 let result = FieldKey::new(input).unwrap();
192 assert_eq!(&result.key, expected);
193
194 let result = FieldKey::try_from(input).unwrap();
196 assert_eq!(&result.key, expected);
197
198 let result = FieldKey::try_from(input.to_string()).unwrap();
200 assert_eq!(&result.key, expected);
201
202 let result = FieldKey::try_from(&input.to_string()).unwrap();
204 assert_eq!(&result.key, expected);
205 }
206
207 #[rstest::rstest]
208 #[case("", "Invalid field key")]
209 #[case(" abc ", "Invalid field key")]
210 #[case("1", "Invalid field key")]
211 #[case("abc.", "Invalid field key")]
212 #[case("abc.123.", "Invalid field key")]
213 #[case("abc.def.", "Invalid field key")]
214 #[case("abc.def.123", "Invalid field key")]
215 #[case("abc..def", "Invalid field key")]
216 #[case(".abc", "Invalid field key")]
217 #[case("1abc", "Invalid field key")]
218 #[case("!", "Invalid field key")]
219 #[case("a!", "Invalid field key")]
220 #[case("abc.!", "Invalid field key")]
221 #[case("abc.d!", "Invalid field key")]
222 #[case(".", "Invalid field key")]
223 #[case("..", "Invalid field key")]
224 fn test_tokens_parse_failure(#[case] input: &str, #[case] expected: &str) {
225 let result = FieldKey::new(input).unwrap_err();
227 assert_eq!(result.to_string(), expected);
228
229 let result = FieldKey::try_from(input).unwrap_err();
231
232 assert_eq!(result.to_string(), expected);
233
234 let result = FieldKey::try_from(input.to_string()).unwrap_err();
236
237 assert_eq!(result.to_string(), expected);
238
239 let result = FieldKey::try_from(&input.to_string()).unwrap_err();
241
242 assert_eq!(result.to_string(), expected);
243 }
244
245 #[rstest::rstest]
246 #[case("test", "test")]
247 #[case("Test", "test")]
248 #[case("_test", "_test")]
249 #[case("_1test", "_1test")]
250 #[case("abc.def", "abc.def")]
251 #[case("abc.def.ghi", "abc.def.ghi")]
252 #[case("abc123.def456", "abc123.def456")]
253 #[case("_abc._def", "_abc._def")]
254 fn test_field_key_display_success(#[case] input: &str, #[case] expected: &str) {
255 let result = FieldKey::new(input).unwrap();
257 assert_eq!(format!("{}", result), expected);
258 }
259
260 #[rstest::rstest]
261 #[case("test", "test")]
262 #[case("test", "Test")]
263 #[case("abc.def", "abc.def")]
264 fn test_field_key_eq(#[case] input: &str, #[case] other: &str) {
265 let input = FieldKey::new(input).unwrap();
266 let other = FieldKey::new(other).unwrap();
267
268 assert_eq!(input, other);
269 }
270
271 #[rstest::rstest]
272 #[case("test", "test1")]
273 #[case("test", "Test1")]
274 #[case("abc.def", "abc")]
275 #[case("abc", "abc.def")]
276 #[case("abc.def", "abc.def1")]
277 fn test_field_key_ne(#[case] input: &str, #[case] other: &str) {
278 let input = FieldKey::new(input).unwrap();
279 let other = FieldKey::new(other).unwrap();
280
281 assert_ne!(input, other);
282 }
283}