Skip to main content

rocketmq_model/utils/
string_utils.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::BTreeMap;
16use std::collections::HashMap;
17
18use cheetah_string::CheetahString;
19
20pub struct StringUtils;
21
22impl StringUtils {
23    #[inline]
24    pub fn is_not_empty_str(s: Option<&str>) -> bool {
25        s.is_some_and(|s| !s.is_empty())
26    }
27
28    #[inline]
29    pub fn is_not_empty_string(s: Option<&String>) -> bool {
30        s.is_some_and(|s| !s.is_empty())
31    }
32
33    #[inline]
34    pub fn is_not_empty_ch_string(s: Option<&CheetahString>) -> bool {
35        s.is_some_and(|s| !s.is_empty())
36    }
37
38    pub fn parse_delay_level(level_string: &str) -> Result<(BTreeMap<i32, i64>, i32), String> {
39        let time_unit_table = HashMap::from([
40            ('s', 1000),
41            ('m', 1000 * 60),
42            ('h', 1000 * 60 * 60),
43            ('d', 1000 * 60 * 60 * 24),
44        ]);
45
46        let mut delay_level_table = BTreeMap::new();
47
48        let level_array: Vec<&str> = level_string.split(' ').collect();
49        let mut max_delay_level = 0;
50
51        for (i, value) in level_array.iter().enumerate() {
52            // let ch = value.chars().last().unwrap().to_string();
53            let ch = match value.chars().last() {
54                None => {
55                    return Err("Empty time unit".to_string());
56                }
57                Some(value) => value,
58            };
59            let tu: i64 = *time_unit_table.get(&ch).ok_or(format!("Unknown time unit: {ch}"))?;
60
61            let level = i as i32 + 1;
62            if level > max_delay_level {
63                max_delay_level = level;
64            }
65
66            let num_str = &value[0..value.len() - 1];
67            let num = num_str.parse::<i64>().map_err(|e| e.to_string())?;
68            let delay_time_millis = tu
69                .checked_mul(num)
70                .ok_or_else(|| format!("Delay level milliseconds overflow: {num}{ch}"))?;
71            delay_level_table.insert(level, delay_time_millis);
72        }
73
74        Ok((delay_level_table, max_delay_level))
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::StringUtils;
81
82    #[test]
83    fn is_not_empty_str_with_some_non_empty_str() {
84        assert!(StringUtils::is_not_empty_str(Some("hello")));
85    }
86
87    #[test]
88    fn is_not_empty_str_with_some_empty_str() {
89        assert!(!StringUtils::is_not_empty_str(Some("")));
90    }
91
92    #[test]
93    fn is_not_empty_str_with_none() {
94        assert!(!StringUtils::is_not_empty_str(None));
95    }
96
97    #[test]
98    fn is_not_empty_string_with_some_non_empty_string() {
99        assert!(StringUtils::is_not_empty_string(Some(&"hello".to_string())));
100    }
101
102    #[test]
103    fn is_not_empty_string_with_some_empty_string() {
104        assert!(!StringUtils::is_not_empty_string(Some(&"".to_string())));
105    }
106
107    #[test]
108    fn is_not_empty_string_with_none() {
109        assert!(!StringUtils::is_not_empty_string(None));
110    }
111
112    #[test]
113    fn parse_delay_level_with_valid_input() {
114        let result = StringUtils::parse_delay_level("1s 2m 3h 4d").unwrap();
115        let (delay_level_table, max_delay_level) = result;
116        assert_eq!(delay_level_table[&1], 1000);
117        assert_eq!(delay_level_table[&2], 2 * 60 * 1000);
118        assert_eq!(delay_level_table[&3], 3 * 60 * 60 * 1000);
119        assert_eq!(delay_level_table[&4], 4 * 24 * 60 * 60 * 1000);
120        assert_eq!(max_delay_level, 4);
121    }
122
123    #[test]
124    fn parse_delay_level_with_unknown_time_unit() {
125        let result = StringUtils::parse_delay_level("1x");
126        assert!(result.is_err());
127        assert_eq!(result.err().unwrap(), "Unknown time unit: x");
128    }
129
130    #[test]
131    fn parse_delay_level_with_invalid_number() {
132        let result = StringUtils::parse_delay_level("1s 2m 3h 4d 5z");
133        assert!(result.is_err());
134        assert_eq!(result.err().unwrap(), "Unknown time unit: z");
135    }
136
137    #[test]
138    fn parse_delay_level_with_empty_string() {
139        let result = StringUtils::parse_delay_level("");
140        assert!(result.is_err());
141    }
142
143    #[test]
144    fn parse_delay_level_with_millisecond_overflow() {
145        let result = StringUtils::parse_delay_level("9223372036854775807s");
146        assert!(result.is_err());
147    }
148
149    #[test]
150    fn parse_delay_level_with_max_valid_delay_time() {
151        let result = StringUtils::parse_delay_level("9223372036854775s").unwrap();
152        let (delay_level_table, max_delay_level) = result;
153        assert_eq!(delay_level_table[&1], i64::MAX - (i64::MAX % 1000));
154        assert_eq!(max_delay_level, 1);
155    }
156}