windjammer_runtime/
strings.rs1pub fn to_upper<S: AsRef<str>>(s: S) -> String {
5 s.as_ref().to_uppercase()
6}
7
8pub fn to_lower<S: AsRef<str>>(s: S) -> String {
10 s.as_ref().to_lowercase()
11}
12
13pub fn trim<S: AsRef<str>>(s: S) -> String {
15 s.as_ref().trim().to_string()
16}
17
18pub fn trim_start<S: AsRef<str>>(s: S) -> String {
20 s.as_ref().trim_start().to_string()
21}
22
23pub fn trim_end<S: AsRef<str>>(s: S) -> String {
25 s.as_ref().trim_end().to_string()
26}
27
28pub fn split<S: AsRef<str>>(s: S, delimiter: &str) -> Vec<String> {
30 s.as_ref().split(delimiter).map(|s| s.to_string()).collect()
31}
32
33pub fn split_lines<S: AsRef<str>>(text: S) -> Vec<String> {
35 let mut lines: Vec<String> = Vec::new();
36 let mut current = String::new();
37 for ch in text.as_ref().chars() {
38 if ch == '\n' {
39 lines.push(current);
40 current = String::new();
41 } else if ch != '\r' {
42 current.push(ch);
43 }
44 }
45 if !current.is_empty() || text.as_ref().ends_with('\n') {
46 lines.push(current);
47 }
48 lines
49}
50
51pub fn split_whitespace<S: AsRef<str>>(line: S) -> Vec<String> {
53 let mut parts: Vec<String> = Vec::new();
54 let mut current = String::new();
55 let mut in_word = false;
56 for ch in line.as_ref().chars() {
57 if ch == ' ' || ch == '\t' {
58 if in_word {
59 parts.push(current);
60 current = String::new();
61 in_word = false;
62 }
63 } else {
64 current.push(ch);
65 in_word = true;
66 }
67 }
68 if in_word {
69 parts.push(current);
70 }
71 parts
72}
73
74pub fn parse_i32<S: AsRef<str>>(s: S) -> i32 {
76 s.as_ref().trim().parse().unwrap_or(0)
77}
78
79pub fn parse_f32<S: AsRef<str>>(s: S) -> f32 {
81 s.as_ref().trim().parse().unwrap_or(0.0)
82}
83
84pub fn parse_bool<S: AsRef<str>>(s: S) -> bool {
86 s.as_ref().trim() == "true"
87}
88
89pub fn byte_at<S: AsRef<str>>(s: S, index: usize) -> u8 {
91 s.as_ref().as_bytes().get(index).copied().unwrap_or(0)
92}
93
94pub fn join(parts: &[String], delimiter: &str) -> String {
96 parts.join(delimiter)
97}
98
99pub fn chars<S: AsRef<str>>(s: S) -> Vec<char> {
101 s.as_ref().chars().collect()
102}
103
104pub fn from_chars(parts: &[char]) -> String {
106 parts.iter().collect()
107}
108
109pub fn substring_chars<S: AsRef<str>>(s: S, start: usize, end: usize) -> String {
111 s.as_ref()
112 .chars()
113 .skip(start)
114 .take(end.saturating_sub(start))
115 .collect()
116}
117
118pub fn contains<S: AsRef<str>>(s: S, substring: &str) -> bool {
120 s.as_ref().contains(substring)
121}
122
123pub fn starts_with<S: AsRef<str>>(s: S, prefix: &str) -> bool {
125 s.as_ref().starts_with(prefix)
126}
127
128pub fn ends_with<S: AsRef<str>>(s: S, suffix: &str) -> bool {
130 s.as_ref().ends_with(suffix)
131}
132
133pub fn replace<S: AsRef<str>>(s: S, from: &str, to: &str) -> String {
135 s.as_ref().replace(from, to)
136}
137
138pub fn substring<S: AsRef<str>>(s: S, start: usize, end: usize) -> String {
140 s.as_ref()
141 .chars()
142 .skip(start)
143 .take(end.saturating_sub(start))
144 .collect()
145}
146
147pub fn len<S: AsRef<str>>(s: S) -> usize {
149 s.as_ref().len()
150}
151
152pub fn is_empty<S: AsRef<str>>(s: S) -> bool {
154 s.as_ref().is_empty()
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_case_conversion() {
163 assert_eq!(to_upper("hello"), "HELLO");
164 assert_eq!(to_lower("WORLD"), "world");
165 }
166
167 #[test]
168 fn test_trim() {
169 assert_eq!(trim(" hello "), "hello");
170 }
171
172 #[test]
173 fn test_split_join() {
174 let parts = split("a,b,c", ",");
175 assert_eq!(parts, vec!["a", "b", "c"]);
176 assert_eq!(join(&parts, "-"), "a-b-c");
177 }
178
179 #[test]
180 fn test_split_lines() {
181 let lines = split_lines("a\nb\r\nc\n");
182 assert_eq!(lines, vec!["a", "b", "c", ""]);
183 }
184
185 #[test]
186 fn test_split_whitespace() {
187 let parts = split_whitespace(" hello\tworld ");
188 assert_eq!(parts, vec!["hello", "world"]);
189 }
190
191 #[test]
192 fn test_parse_i32() {
193 assert_eq!(parse_i32("-42"), -42);
194 assert_eq!(parse_i32(" 7 "), 7);
195 assert_eq!(parse_i32("bad"), 0);
196 }
197
198 #[test]
199 fn test_parse_f32() {
200 assert!((parse_f32("3.14") - 3.14).abs() < 0.001);
201 assert!((parse_f32("-2.5") - (-2.5)).abs() < 0.001);
202 }
203
204 #[test]
205 fn test_parse_bool() {
206 assert!(parse_bool("true"));
207 assert!(!parse_bool("false"));
208 }
209
210 #[test]
211 fn test_byte_at() {
212 assert_eq!(byte_at("ABC", 0), b'A');
213 assert_eq!(byte_at("ABC", 2), b'C');
214 assert_eq!(byte_at("ABC", 99), 0);
215 }
216
217 #[test]
218 fn test_chars_and_from_chars() {
219 let c = chars("hello");
220 assert_eq!(c, vec!['h', 'e', 'l', 'l', 'o']);
221 assert_eq!(from_chars(&c), "hello");
222 assert_eq!(substring_chars("hello", 1, 4), "ell");
223 }
224}