baichun_framework_core/utils/
string.rs

1// ruoyi-common/src/utils/string.rs
2//! 字符串处理工具模块
3use rand::{distributions::Alphanumeric, Rng};
4use regex::Regex;
5use serde::Deserialize;
6use serde_json;
7use std::{borrow::Cow, collections::HashMap};
8use uuid::Uuid;
9
10/// 字符串工具
11pub struct StringUtils;
12
13impl StringUtils {
14    /// 驼峰转下划线
15    pub fn camel_to_snake(s: &str) -> String {
16        let mut result = String::with_capacity(s.len());
17        let mut chars = s.chars().peekable();
18        while let Some(c) = chars.next() {
19            if c.is_uppercase() {
20                if !result.is_empty() {
21                    result.push('_');
22                }
23                result.push(c.to_lowercase().next().unwrap());
24            } else {
25                result.push(c);
26            }
27        }
28        result
29    }
30
31    /// 下划线转驼峰
32    pub fn snake_to_camel(s: &str) -> String {
33        let mut result = String::with_capacity(s.len());
34        let mut capitalize_next = false;
35        for c in s.chars() {
36            if c == '_' {
37                capitalize_next = true;
38            } else if capitalize_next {
39                result.push(c.to_uppercase().next().unwrap());
40                capitalize_next = false;
41            } else {
42                result.push(c);
43            }
44        }
45        result
46    }
47
48    /// 首字母大写
49    pub fn capitalize(s: &str) -> String {
50        let mut chars = s.chars();
51        match chars.next() {
52            None => String::new(),
53            Some(first) => first.to_uppercase().chain(chars).collect(),
54        }
55    }
56
57    /// 首字母小写
58    pub fn uncapitalize(s: &str) -> String {
59        let mut chars = s.chars();
60        match chars.next() {
61            None => String::new(),
62            Some(first) => first.to_lowercase().chain(chars).collect(),
63        }
64    }
65
66    /// 生成UUID
67    pub fn uuid() -> String {
68        Uuid::new_v4().to_string()
69    }
70
71    /// 生成随机字符串
72    pub fn random_string(length: usize) -> String {
73        rand::thread_rng()
74            .sample_iter(&Alphanumeric)
75            .take(length)
76            .map(char::from)
77            .collect()
78    }
79
80    /// 下划线转驼峰
81    pub fn to_camel_case(s: &str) -> String {
82        let mut result = String::new();
83        let mut capitalize_next = false;
84
85        for c in s.chars() {
86            if c == '_' {
87                capitalize_next = true;
88            } else if capitalize_next {
89                result.push(c.to_ascii_uppercase());
90                capitalize_next = false;
91            } else {
92                result.push(c);
93            }
94        }
95
96        result
97    }
98
99    /// 驼峰转下划线
100    ///
101    pub fn to_snake_case(s: &str) -> String {
102        let mut result = String::new();
103
104        for (i, c) in s.chars().enumerate() {
105            if c.is_uppercase() {
106                if i > 0 {
107                    result.push('_');
108                }
109                result.push(c.to_ascii_lowercase());
110            } else {
111                result.push(c);
112            }
113        }
114
115        result
116    }
117
118    /// 截取字符串
119    pub fn substring(s: &str, start: usize, end: Option<usize>) -> Cow<str> {
120        let len = s.len();
121        let end = end.unwrap_or(len);
122
123        if start >= len || end <= start {
124            return Cow::Borrowed("");
125        }
126
127        let s_bytes = s.as_bytes();
128        let mut start_idx = start;
129        let mut end_idx = end.min(len);
130
131        // 确保不会在UTF-8字符的中间截断
132        while start_idx < len && !Self::is_char_boundary(s, start_idx) {
133            start_idx += 1;
134        }
135        while end_idx > 0 && !Self::is_char_boundary(s, end_idx) {
136            end_idx -= 1;
137        }
138
139        if start_idx >= end_idx {
140            return Cow::Borrowed("");
141        }
142
143        Cow::Borrowed(unsafe { std::str::from_utf8_unchecked(&s_bytes[start_idx..end_idx]) })
144    }
145
146    /// 检查字符边界
147    fn is_char_boundary(s: &str, idx: usize) -> bool {
148        if idx == 0 || idx == s.len() {
149            return true;
150        }
151        let b = s.as_bytes()[idx];
152        b & 0xc0 != 0x80
153    }
154
155    /// 缓存正则表达式
156    pub fn regex_match(s: &str, pattern: &Regex) -> bool {
157        pattern.is_match(s)
158    }
159
160    pub fn regex_from_pattern(pattern: &str) -> Regex {
161        Regex::new(pattern).unwrap()
162    }
163
164    pub fn option_is_empty(option: &Option<String>) -> bool {
165        option.is_none() || option.as_ref().unwrap().trim().is_empty()
166    }
167
168    pub fn string_to_vec_u8(s: &str) -> Vec<u8> {
169        s.as_bytes().to_vec()
170    }
171
172    pub fn deserialize_str_to_i32<'de, D>(
173        deserializer: D,
174    ) -> std::result::Result<Option<i32>, D::Error>
175    where
176        D: serde::Deserializer<'de>,
177    {
178        // 使用 serde_json::Value 接收反序列化结果,它可以处理各种类型
179        let value = serde_json::Value::deserialize(deserializer)?;
180
181        if value.is_null() {
182            return Ok(None);
183        } else if let Some(i) = value.as_i64() {
184            // 处理整数
185            return Ok(Some(i as i32));
186        } else if let Some(s) = value.as_str() {
187            // 处理字符串,尝试解析为整数
188            if let Ok(i) = s.parse::<i32>() {
189                return Ok(Some(i));
190            }
191        }
192        // 处理其他情况
193        Ok(None)
194    }
195
196    pub fn serialize_i32_to_string<S>(value: &Option<i32>, serializer: S) -> Result<S::Ok, S::Error>
197    where
198        S: serde::Serializer,
199    {
200        match value {
201            Some(i) => serializer.serialize_str(&i.to_string()),
202            None => serializer.serialize_none(),
203        }
204    }
205
206    pub fn serialize_vec_u8_to_string<S>(
207        value: &Option<Vec<u8>>,
208        serializer: S,
209    ) -> Result<S::Ok, S::Error>
210    where
211        S: serde::Serializer,
212    {
213        match value {
214            Some(v) => serializer.serialize_str(String::from_utf8_lossy(v).as_ref()),
215            None => serializer.serialize_none(),
216        }
217    }
218
219    pub fn redis_info_to_map(info: &str) -> HashMap<String, String> {
220        let mut map = HashMap::new();
221        for line in info.lines() {
222            if line.is_empty() || line.starts_with('#') {
223                continue;
224            }
225            let mut parts = line.splitn(2, ':');
226            if let (Some(key), Some(value)) = (parts.next(), parts.next()) {
227                map.insert(key.to_string(), value.to_string());
228            }
229        }
230        map
231    }
232
233    pub fn redis_command_stats_to_map(info: &str) -> Vec<HashMap<String, String>> {
234        let mut list = Vec::new();
235        for line in info.lines() {
236            let mut parts = line.splitn(2, ':');
237            if let (Some(key), Some(value)) = (parts.next(), parts.next()) {
238                let mut map = HashMap::new();
239                // 将key中的前缀去掉
240                let key = key.replace("cmdstat_", "");
241                // 截取出value中的calls部分
242                let value = value
243                    .split(',')
244                    .find(|part| part.trim().starts_with("calls="))
245                    .map(|part| part.trim().replace("calls=", ""))
246                    .unwrap_or_else(|| "0".to_string());
247                map.insert("name".to_string(), key.to_string());
248                map.insert("value".to_string(), value);
249                list.push(map);
250            }
251        }
252        list
253    }
254}