1use std::collections::hash_map::Entry;
2use std::collections::HashMap;
3use std::fmt;
4use std::ops::{Deref, DerefMut};
5use std::str::FromStr;
6
7use derive_more::{Display, Error, From, IntoIterator};
8use serde::de::Deserializer;
9use serde::ser::Serializer;
10use serde::{Deserialize, Serialize};
11
12use crate::common::utils::{deserialize_from_str, serialize_to_str};
13
14#[derive(Clone, Debug, From, IntoIterator, PartialEq, Eq)]
16pub struct Map(HashMap<String, String>);
17
18impl Map {
19 pub fn new() -> Self {
20 Self(HashMap::new())
21 }
22
23 pub fn into_map(self) -> HashMap<String, String> {
24 self.0
25 }
26
27 pub fn merge(&mut self, other: Map, keep: bool) {
60 for (key, value) in other {
61 match self.0.entry(key) {
62 Entry::Occupied(_) if keep => continue,
64
65 Entry::Occupied(mut x) => {
67 x.insert(value);
68 }
69
70 Entry::Vacant(x) => {
72 x.insert(value);
73 }
74 }
75 }
76 }
77}
78
79impl Default for Map {
80 fn default() -> Self {
81 Self::new()
82 }
83}
84
85impl Deref for Map {
86 type Target = HashMap<String, String>;
87
88 fn deref(&self) -> &Self::Target {
89 &self.0
90 }
91}
92
93impl DerefMut for Map {
94 fn deref_mut(&mut self) -> &mut Self::Target {
95 &mut self.0
96 }
97}
98
99impl fmt::Display for Map {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 let len = self.0.len();
103 for (i, (key, value)) in self.0.iter().enumerate() {
104 write!(f, "{}=\"{}\"", key, encode_value(value))?;
105
106 if i + 1 < len {
108 write!(f, ",")?;
109 }
110 }
111 Ok(())
112 }
113}
114
115#[derive(Clone, Debug, Display, Error)]
116pub enum MapParseError {
117 #[display(fmt = "Missing = after key ('{key}')")]
118 MissingEqualsAfterKey { key: String },
119
120 #[display(fmt = "Key ('{key}') must start with alphabetic character")]
121 KeyMustStartWithAlphabeticCharacter { key: String },
122
123 #[display(fmt = "Missing closing \" for value")]
124 MissingClosingQuoteForValue,
125}
126
127impl FromStr for Map {
128 type Err = MapParseError;
129
130 fn from_str(s: &str) -> Result<Self, Self::Err> {
133 let mut map = HashMap::new();
134
135 let mut s = s.trim();
136 while !s.is_empty() {
137 let (key, tail) = s
139 .split_once('=')
140 .ok_or_else(|| MapParseError::MissingEqualsAfterKey { key: s.to_string() })?;
141
142 let key = key.trim();
144
145 if !key.starts_with(char::is_alphabetic) {
146 return Err(MapParseError::KeyMustStartWithAlphabeticCharacter {
147 key: key.to_string(),
148 });
149 }
150
151 let tail = tail.trim_start();
153
154 let (value, tail) = match tail.strip_prefix('"') {
156 Some(tail) => {
158 let mut backslash_cnt: usize = 0;
159 let mut split_idx = None;
160 for (i, b) in tail.bytes().enumerate() {
161 if b == b'\\' {
163 backslash_cnt += 1;
164
165 } else if b == b'"' && backslash_cnt % 2 == 0 {
168 split_idx = Some(i);
169 break;
170
171 } else {
174 backslash_cnt = 0;
175 }
176 }
177
178 match split_idx {
179 Some(i) => {
180 let (value, tail) = tail.split_at(i);
184 let tail = &tail[1..].trim_start();
185
186 let tail = tail.strip_prefix(',').unwrap_or(tail).trim_start();
188
189 (value, tail)
190 }
191 None => return Err(MapParseError::MissingClosingQuoteForValue),
192 }
193 }
194
195 None => match tail.split_once(',') {
197 Some((value, tail)) => (value.trim(), tail),
198 None => (tail.trim(), ""),
199 },
200 };
201
202 map.insert(key.to_string(), decode_value(value));
204 s = tail.trim();
205 }
206
207 Ok(Self(map))
208 }
209}
210
211#[inline]
215fn encode_value(value: &str) -> String {
216 value.replace('\\', "\\\\").replace('"', "\\\"")
219}
220
221#[inline]
225fn decode_value(value: &str) -> String {
226 value.replace("\\\\", "\\").replace("\\\"", "\"")
227}
228
229impl Serialize for Map {
230 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
231 where
232 S: Serializer,
233 {
234 serialize_to_str(self, serializer)
235 }
236}
237
238impl<'de> Deserialize<'de> for Map {
239 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
240 where
241 D: Deserializer<'de>,
242 {
243 deserialize_from_str(deserializer)
244 }
245}
246
247#[macro_export]
255macro_rules! map {
256 ($($key:literal -> $value:literal),* $(,)?) => {{
257 let mut _map = ::std::collections::HashMap::new();
258
259 $(
260 _map.insert($key.to_string(), $value.to_string());
261 )*
262
263 $crate::common::Map::from(_map)
264 }};
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270
271 #[test]
272 fn should_support_being_parsed_from_str() {
273 let map = " ".parse::<Map>().unwrap();
275 assert_eq!(map, map!());
276
277 let map = "key=value".parse::<Map>().unwrap();
279 assert_eq!(map, map!("key" -> "value"));
280
281 let map = "key.with-characters@=value".parse::<Map>().unwrap();
283 assert_eq!(map, map!("key.with-characters@" -> "value"));
284
285 let map = "key=value.has -@#$".parse::<Map>().unwrap();
287 assert_eq!(map, map!("key" -> "value.has -@#$"));
288
289 let map = r#"key=",,,,""#.parse::<Map>().unwrap();
291 assert_eq!(map, map!("key" -> ",,,,"));
292
293 let map = " key = value ".parse::<Map>().unwrap();
295 assert_eq!(map, map!("key" -> "value"));
296
297 let map = r#" key = " value " "#.parse::<Map>().unwrap();
299 assert_eq!(map, map!("key" -> " value "));
300
301 let map = "key=value,key2=value2".parse::<Map>().unwrap();
303 assert_eq!(map, map!("key" -> "value", "key2" -> "value2"));
304
305 let map = r#"key="value one",key2=value2"#.parse::<Map>().unwrap();
307 assert_eq!(map, map!("key" -> "value one", "key2" -> "value2"));
308
309 let map = r#"key=value,key2="value two""#.parse::<Map>().unwrap();
310 assert_eq!(map, map!("key" -> "value", "key2" -> "value two"));
311
312 let map = r#"key="value one",key2="value two""#.parse::<Map>().unwrap();
313 assert_eq!(map, map!("key" -> "value one", "key2" -> "value two"));
314
315 let map = r#"key="1,2,3",key2="4,5,6""#.parse::<Map>().unwrap();
316 assert_eq!(map, map!("key" -> "1,2,3", "key2" -> "4,5,6"));
317
318 let map = "key=value,".parse::<Map>().unwrap();
320 assert_eq!(map, map!("key" -> "value"));
321 let map = r#"key=",value,","#.parse::<Map>().unwrap();
322 assert_eq!(map, map!("key" -> ",value,"));
323
324 let map = "key=value key2=value2".parse::<Map>().unwrap();
326 assert_eq!(map, map!("key" -> "value key2=value2"));
327
328 let map = r#"key="\"va\"lue\"""#.parse::<Map>().unwrap();
330 assert_eq!(map, map!("key" -> r#""va"lue""#));
331
332 let map = r#"key="a\\b\\c""#.parse::<Map>().unwrap();
334 assert_eq!(map, map!("key" -> r"a\b\c"));
335
336 let map = r#"key="\"\\\"\\\"""#.parse::<Map>().unwrap();
338 assert_eq!(map, map!("key" -> r#""\"\""#));
339
340 let _ = ",".parse::<Map>().unwrap_err();
342 let _ = ",key=value".parse::<Map>().unwrap_err();
343 let _ = "key=value,key2".parse::<Map>().unwrap_err();
344 }
345
346 #[test]
347 fn should_support_being_displayed_as_a_string() {
348 let map = map!().to_string();
349 assert_eq!(map, "");
350
351 let map = map!("key" -> "value").to_string();
352 assert_eq!(map, r#"key="value""#);
353
354 let map = map!("key" -> r#""va"lue""#).to_string();
356 assert_eq!(map, r#"key="\"va\"lue\"""#);
357
358 let map = map!("key" -> r"a\b\c").to_string();
360 assert_eq!(map, r#"key="a\\b\\c""#);
361
362 let map = map!("key" -> r#""\"\""#).to_string();
364 assert_eq!(map, r#"key="\"\\\"\\\"""#);
365
366 let map = map!("key" -> "value", "key2" -> "value2").to_string();
368 assert!(
369 map == r#"key="value",key2="value2""# || map == r#"key2="value2",key="value""#,
370 "{:?}",
371 map
372 );
373
374 let map = map!("key" -> ",", "key2" -> ",,").to_string();
376 assert!(
377 map == r#"key=",",key2=",,""# || map == r#"key2=",,",key=",""#,
378 "{:?}",
379 map
380 );
381 }
382}