rocketmq_model/common/attribute/
attribute_parser.rs1use std::collections::HashMap;
16
17const ATTR_ARRAY_SEPARATOR_COMMA: &str = ",";
18const ATTR_KEY_VALUE_EQUAL_SIGN: &str = "=";
19const ATTR_ADD_PLUS_SIGN: &str = "+";
20const ATTR_DELETE_MINUS_SIGN: &str = "-";
21
22#[derive(Debug)]
23pub struct AttributeParser;
24
25impl AttributeParser {
26 pub fn parse_to_map(attributes_modification: &str) -> Result<HashMap<String, String>, String> {
27 if attributes_modification.is_empty() {
28 return Ok(HashMap::new());
29 }
30
31 let mut attributes = HashMap::new();
32 let kvs: Vec<&str> = attributes_modification.split(ATTR_ARRAY_SEPARATOR_COMMA).collect();
33 for kv in kvs {
34 let mut key = String::new();
35 let mut value = String::new();
36 if kv.contains(ATTR_KEY_VALUE_EQUAL_SIGN) {
37 let splits: Vec<&str> = kv.split(ATTR_KEY_VALUE_EQUAL_SIGN).collect();
38 key.push_str(splits[0]);
39 value.push_str(splits[1]);
40 if !key.contains(ATTR_ADD_PLUS_SIGN) {
41 return Err(format!("add/alter attribute format is wrong: {key}"));
42 }
43 } else {
44 key.push_str(kv);
45 if !key.contains(ATTR_DELETE_MINUS_SIGN) {
46 return Err(format!("delete attribute format is wrong: {key}",));
47 }
48 }
49 if attributes.insert(key.clone(), value).is_some() {
50 return Err(format!("key duplication: {key}",));
51 }
52 }
53 Ok(attributes)
54 }
55
56 pub fn parse_to_string(attributes: &HashMap<String, String>) -> String {
57 if attributes.is_empty() {
58 return String::new();
59 }
60
61 let mut kvs: Vec<String> = Vec::new();
62 for (key, value) in attributes {
63 if value.is_empty() {
64 kvs.push(key.clone());
65 } else {
66 kvs.push(format!("{key}{ATTR_KEY_VALUE_EQUAL_SIGN}{value}"));
67 }
68 }
69 kvs.join(ATTR_ARRAY_SEPARATOR_COMMA)
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn test_parse_to_map_empty_string() {
79 let result = AttributeParser::parse_to_map("");
80 assert_eq!(result.unwrap(), HashMap::new());
81 }
82
83 #[test]
84 fn test_parse_to_map_valid_attributes() {
85 let input = "+key1=value1,+key2=value2,-key3";
86 let mut expected = HashMap::new();
87 expected.insert("+key1".to_string(), "value1".to_string());
88 expected.insert("+key2".to_string(), "value2".to_string());
89 expected.insert("-key3".to_string(), "".to_string());
90
91 let result = AttributeParser::parse_to_map(input);
92 assert_eq!(result.unwrap(), expected);
93 }
94
95 #[test]
96 fn test_parse_to_map_add_attribute_format_error() {
97 let input = "key1=value1,+key2=value2";
98 let result = AttributeParser::parse_to_map(input);
99 assert_eq!(
100 result.unwrap_err(),
101 "add/alter attribute format is wrong: key1".to_string()
102 );
103 }
104
105 #[test]
106 fn test_parse_to_map_delete_attribute_format_error() {
107 let input = "+key1=value1,key2";
108 let result = AttributeParser::parse_to_map(input);
109 assert_eq!(
110 result.unwrap_err(),
111 "delete attribute format is wrong: key2".to_string()
112 );
113 }
114
115 #[test]
116 fn test_parse_to_map_key_duplication_error() {
117 let input = "+key1=value1,+key1=value2";
118 let result = AttributeParser::parse_to_map(input);
119 assert_eq!(result.unwrap_err(), "key duplication: +key1".to_string());
120 }
121
122 #[test]
123 fn test_parse_to_string_empty_map() {
124 let attributes = HashMap::new();
125 let result = AttributeParser::parse_to_string(&attributes);
126 assert_eq!(result, "");
127 }
128
129 #[test]
130 fn test_parse_to_string_nonempty_operations_round_trip() {
131 for (operations, mut expected) in [
132 (
133 vec![("+key1", "hello 世界"), ("+key2", "value2"), ("-key3", "")],
134 vec!["+key1=hello 世界", "+key2=value2", "-key3"],
135 ),
136 (vec![("+key1", "hello 世界")], vec!["+key1=hello 世界"]),
137 (vec![("-key3", "")], vec!["-key3"]),
138 ] {
139 let attributes = operations
140 .iter()
141 .map(|(key, value)| (key.to_string(), value.to_string()))
142 .collect();
143 let result = AttributeParser::parse_to_string(&attributes);
144 let mut tokens: Vec<_> = result.split(',').collect();
145 tokens.sort_unstable();
146 expected.sort_unstable();
147 assert_eq!(tokens, expected);
148 assert_eq!(AttributeParser::parse_to_map(&result).unwrap(), attributes);
149 }
150 }
151}