baichun_framework_core/utils/
string.rs1use rand::{distributions::Alphanumeric, Rng};
4use regex::Regex;
5use serde::Deserialize;
6use serde_json;
7use std::{borrow::Cow, collections::HashMap};
8use uuid::Uuid;
9
10pub struct StringUtils;
12
13impl StringUtils {
14 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 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 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 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 pub fn uuid() -> String {
68 Uuid::new_v4().to_string()
69 }
70
71 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 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 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 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 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 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 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 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 return Ok(Some(i as i32));
186 } else if let Some(s) = value.as_str() {
187 if let Ok(i) = s.parse::<i32>() {
189 return Ok(Some(i));
190 }
191 }
192 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 let key = key.replace("cmdstat_", "");
241 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}