rocketmq_common/common/attribute/
attribute_parser.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17use std::collections::HashMap;
18use std::string::ToString;
19
20const ATTR_ARRAY_SEPARATOR_COMMA: &str = ",";
21const ATTR_KEY_VALUE_EQUAL_SIGN: &str = "=";
22const ATTR_ADD_PLUS_SIGN: &str = "+";
23const ATTR_DELETE_MINUS_SIGN: &str = "-";
24
25#[derive(Debug)]
26pub struct AttributeParser;
27
28impl AttributeParser {
29    pub fn parse_to_map(attributes_modification: &str) -> Result<HashMap<String, String>, String> {
30        if attributes_modification.is_empty() {
31            return Ok(HashMap::new());
32        }
33
34        let mut attributes = HashMap::new();
35        let kvs: Vec<&str> = attributes_modification
36            .split(ATTR_ARRAY_SEPARATOR_COMMA)
37            .collect();
38        for kv in kvs {
39            let mut key = String::new();
40            let mut value = String::new();
41            if kv.contains(ATTR_KEY_VALUE_EQUAL_SIGN) {
42                let splits: Vec<&str> = kv.split(ATTR_KEY_VALUE_EQUAL_SIGN).collect();
43                key.push_str(splits[0]);
44                value.push_str(splits[1]);
45                if !key.contains(ATTR_ADD_PLUS_SIGN) {
46                    return Err(format!("add/alter attribute format is wrong: {key}"));
47                }
48            } else {
49                key.push_str(kv);
50                if !key.contains(ATTR_DELETE_MINUS_SIGN) {
51                    return Err(format!("delete attribute format is wrong: {key}",));
52                }
53            }
54            if attributes.insert(key.clone(), value).is_some() {
55                return Err(format!("key duplication: {key}",));
56            }
57        }
58        Ok(attributes)
59    }
60
61    pub fn parse_to_string(attributes: &HashMap<String, String>) -> String {
62        if attributes.is_empty() {
63            return String::new();
64        }
65
66        let mut kvs: Vec<String> = Vec::new();
67        for (key, value) in attributes {
68            if value.is_empty() {
69                kvs.push(key.clone());
70            } else {
71                kvs.push(format!("{key}{ATTR_KEY_VALUE_EQUAL_SIGN}{value}"));
72            }
73        }
74        kvs.join(ATTR_ARRAY_SEPARATOR_COMMA)
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn test_parse_to_map_empty_string() {
84        let result = AttributeParser::parse_to_map("");
85        assert_eq!(result.unwrap(), HashMap::new());
86    }
87
88    #[test]
89    fn test_parse_to_map_valid_attributes() {
90        let input = "+key1=value1,+key2=value2,-key3";
91        let mut expected = HashMap::new();
92        expected.insert("+key1".to_string(), "value1".to_string());
93        expected.insert("+key2".to_string(), "value2".to_string());
94        expected.insert("-key3".to_string(), "".to_string());
95
96        let result = AttributeParser::parse_to_map(input);
97        assert_eq!(result.unwrap(), expected);
98    }
99
100    #[test]
101    fn test_parse_to_map_add_attribute_format_error() {
102        let input = "key1=value1,+key2=value2";
103        let result = AttributeParser::parse_to_map(input);
104        assert_eq!(
105            result.unwrap_err(),
106            "add/alter attribute format is wrong: key1".to_string()
107        );
108    }
109
110    #[test]
111    fn test_parse_to_map_delete_attribute_format_error() {
112        let input = "+key1=value1,key2";
113        let result = AttributeParser::parse_to_map(input);
114        assert_eq!(
115            result.unwrap_err(),
116            "delete attribute format is wrong: key2".to_string()
117        );
118    }
119
120    #[test]
121    fn test_parse_to_map_key_duplication_error() {
122        let input = "+key1=value1,+key1=value2";
123        let result = AttributeParser::parse_to_map(input);
124        assert_eq!(result.unwrap_err(), "key duplication: +key1".to_string());
125    }
126
127    #[test]
128    fn test_parse_to_string_empty_map() {
129        let attributes = HashMap::new();
130        let result = AttributeParser::parse_to_string(&attributes);
131        assert_eq!(result, "");
132    }
133
134    // #[test]
135    // fn test_parse_to_string_valid_map() {
136    //     let mut attributes = HashMap::new();
137    //     attributes.insert("+key1".to_string(), "value1".to_string());
138    //     attributes.insert("+key2".to_string(), "value2".to_string());
139    //     attributes.insert("-key3".to_string(), "".to_string());
140
141    //     let result = AttributeParser::parse_to_string(&attributes);
142    //     assert!(
143    //         result == "+key1=value1,+key2=value2,-key3"
144    //             || result == "+key2=value2,+key1=value1,-key3"
145    //     );
146    // }
147}