1use std::time::Duration;
4
5pub fn parse_duration(s: &str) -> Option<Duration> {
16 let s = s.trim();
17 if let Some(num) = s.strip_suffix("ms") {
18 num.parse::<u64>().ok().map(Duration::from_millis)
19 } else if let Some(num) = s.strip_suffix('s') {
20 num.parse::<u64>().ok().map(Duration::from_secs)
21 } else if let Some(num) = s.strip_suffix('m') {
22 num.parse::<u64>().ok().map(|m| Duration::from_secs(m * 60))
23 } else if let Some(num) = s.strip_suffix('h') {
24 num.parse::<u64>()
25 .ok()
26 .map(|h| Duration::from_secs(h * 3600))
27 } else if let Some(num) = s.strip_suffix('d') {
28 num.parse::<u64>()
29 .ok()
30 .map(|d| Duration::from_secs(d * 86400))
31 } else {
32 s.parse::<u64>().ok().map(Duration::from_secs)
33 }
34}
35
36pub fn parse_size(s: &str) -> Option<usize> {
46 let s = s.trim().to_lowercase();
47 if let Some(num) = s.strip_suffix("gb") {
48 num.trim()
49 .parse::<usize>()
50 .ok()
51 .map(|n| n * 1024 * 1024 * 1024)
52 } else if let Some(num) = s.strip_suffix("mb") {
53 num.trim().parse::<usize>().ok().map(|n| n * 1024 * 1024)
54 } else if let Some(num) = s.strip_suffix("kb") {
55 num.trim().parse::<usize>().ok().map(|n| n * 1024)
56 } else if let Some(num) = s.strip_suffix('b') {
57 num.trim().parse::<usize>().ok()
58 } else {
59 s.parse::<usize>().ok()
60 }
61}
62
63pub fn to_pascal_case(s: &str) -> String {
65 s.split('_')
66 .map(|part| {
67 let mut chars = part.chars();
68 match chars.next() {
69 None => String::new(),
70 Some(first) => first.to_uppercase().chain(chars).collect(),
71 }
72 })
73 .collect()
74}
75
76pub fn to_snake_case(s: &str) -> String {
78 let mut result = String::new();
79 for (i, c) in s.chars().enumerate() {
80 if c.is_uppercase() {
81 if i > 0 {
82 result.push('_');
83 }
84 result.extend(c.to_lowercase());
85 } else {
86 result.push(c);
87 }
88 }
89 result
90}
91
92pub fn to_camel_case(s: &str) -> String {
94 let mut result = String::new();
95 let mut capitalize_next = false;
96 for c in s.chars() {
97 if c == '_' {
98 capitalize_next = true;
99 } else if capitalize_next {
100 result.extend(c.to_uppercase());
101 capitalize_next = false;
102 } else {
103 result.push(c);
104 }
105 }
106 result
107}
108
109#[cfg(test)]
110#[allow(clippy::unwrap_used, clippy::indexing_slicing)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_parse_duration_milliseconds() {
116 assert_eq!(parse_duration("100ms"), Some(Duration::from_millis(100)));
117 assert_eq!(parse_duration("1000ms"), Some(Duration::from_millis(1000)));
118 }
119
120 #[test]
121 fn test_parse_duration_seconds() {
122 assert_eq!(parse_duration("30s"), Some(Duration::from_secs(30)));
123 assert_eq!(parse_duration("60s"), Some(Duration::from_secs(60)));
124 }
125
126 #[test]
127 fn test_parse_duration_minutes() {
128 assert_eq!(parse_duration("5m"), Some(Duration::from_secs(300)));
129 assert_eq!(parse_duration("10m"), Some(Duration::from_secs(600)));
130 }
131
132 #[test]
133 fn test_parse_duration_hours() {
134 assert_eq!(parse_duration("1h"), Some(Duration::from_secs(3600)));
135 assert_eq!(parse_duration("24h"), Some(Duration::from_secs(86400)));
136 }
137
138 #[test]
139 fn test_parse_duration_days() {
140 assert_eq!(parse_duration("1d"), Some(Duration::from_secs(86400)));
141 assert_eq!(parse_duration("7d"), Some(Duration::from_secs(604800)));
142 }
143
144 #[test]
145 fn test_parse_duration_bare_number() {
146 assert_eq!(parse_duration("60"), Some(Duration::from_secs(60)));
147 assert_eq!(parse_duration("3600"), Some(Duration::from_secs(3600)));
148 }
149
150 #[test]
151 fn test_parse_duration_whitespace() {
152 assert_eq!(parse_duration(" 30s "), Some(Duration::from_secs(30)));
153 }
154
155 #[test]
156 fn test_parse_duration_invalid() {
157 assert_eq!(parse_duration("invalid"), None);
158 assert_eq!(parse_duration("abc123"), None);
159 assert_eq!(parse_duration(""), None);
160 }
161
162 #[test]
163 fn test_parse_size_kilobytes() {
164 assert_eq!(parse_size("100kb"), Some(100 * 1024));
165 assert_eq!(parse_size("512KB"), Some(512 * 1024));
166 }
167
168 #[test]
169 fn test_parse_size_megabytes() {
170 assert_eq!(parse_size("20mb"), Some(20 * 1024 * 1024));
171 assert_eq!(parse_size("100MB"), Some(100 * 1024 * 1024));
172 }
173
174 #[test]
175 fn test_parse_size_gigabytes() {
176 assert_eq!(parse_size("1gb"), Some(1024 * 1024 * 1024));
177 assert_eq!(parse_size("2GB"), Some(2 * 1024 * 1024 * 1024));
178 }
179
180 #[test]
181 fn test_parse_size_bytes() {
182 assert_eq!(parse_size("1024b"), Some(1024));
183 assert_eq!(parse_size("0b"), Some(0));
184 }
185
186 #[test]
187 fn test_parse_size_bare_number() {
188 assert_eq!(parse_size("1048576"), Some(1048576));
189 }
190
191 #[test]
192 fn test_parse_size_whitespace() {
193 assert_eq!(parse_size(" 20mb "), Some(20 * 1024 * 1024));
194 }
195
196 #[test]
197 fn test_parse_size_invalid() {
198 assert_eq!(parse_size("invalid"), None);
199 assert_eq!(parse_size("abc123"), None);
200 assert_eq!(parse_size(""), None);
201 }
202
203 #[test]
204 fn test_to_snake_case() {
205 assert_eq!(to_snake_case("GetUser"), "get_user");
206 assert_eq!(to_snake_case("ListAllProjects"), "list_all_projects");
207 assert_eq!(to_snake_case("Simple"), "simple");
208 assert_eq!(to_snake_case("ProjectStatus"), "project_status");
209 }
210
211 #[test]
212 fn test_to_pascal_case() {
213 assert_eq!(to_pascal_case("get_user"), "GetUser");
214 assert_eq!(to_pascal_case("list_all_projects"), "ListAllProjects");
215 assert_eq!(to_pascal_case("simple"), "Simple");
216 }
217
218 #[test]
219 fn test_to_camel_case() {
220 assert_eq!(to_camel_case("get_user"), "getUser");
221 assert_eq!(to_camel_case("list_all_projects"), "listAllProjects");
222 assert_eq!(to_camel_case("simple"), "simple");
223 }
224}