1pub mod align;
29pub mod case;
30pub mod classify;
31pub mod concat;
32pub mod regex_ops;
33pub mod search;
34pub mod split_join;
35pub mod str_ops;
36pub mod string_array;
37pub mod strip;
38
39pub use string_array::{StringArray, StringArray1, StringArray2, array};
41
42pub use align::{center, ljust, ljust_with, rjust, rjust_with, zfill};
44pub use case::{capitalize, lower, title, upper};
45pub use classify::{isalnum, isalpha, isdigit, islower, isnumeric, isspace, istitle, isupper};
46pub use concat::{add, add_same, multiply};
47pub use regex_ops::{extract, extract_compiled, match_, match_compiled};
48pub use regex::Regex;
51pub use search::{count, endswith, find, replace, startswith};
52pub use split_join::{join, join_array, split};
53pub use str_ops::{equal, greater, greater_equal, less, less_equal, not_equal, str_len, swapcase};
54pub use strip::{lstrip, rstrip, strip};
55
56#[cfg(test)]
57mod integration_tests {
58 use super::*;
59
60 #[test]
61 fn ac1_upper() {
62 let a = array(&["hello", "world"]).unwrap();
64 let b = upper(&a).unwrap();
65 assert_eq!(b.as_slice(), &["HELLO", "WORLD"]);
66 }
67
68 #[test]
69 fn ac2_add_broadcast_scalar() {
70 let a = array(&["hello", "world"]).unwrap();
72 let b = array(&["!"]).unwrap();
73 let c = add(&a, &b).unwrap();
74 assert_eq!(c.as_slice(), &["hello!", "world!"]);
75 }
76
77 #[test]
78 fn ac3_find_indices() {
79 let a = array(&["hello", "world"]).unwrap();
81 let b = find(&a, "ll").unwrap();
82 let data = b.as_slice().unwrap();
83 assert_eq!(data, &[2_i64, -1_i64]);
84 }
85
86 #[test]
87 fn ac4_split() {
88 let a = array(&["a-b", "c-d"]).unwrap();
90 let result = split(&a, "-").unwrap();
91 assert_eq!(
92 result,
93 vec![
94 vec!["a".to_string(), "b".to_string()],
95 vec!["c".to_string(), "d".to_string()],
96 ]
97 );
98 }
99
100 #[test]
101 fn ac5_regex() {
102 let a = array(&["abc123", "def", "ghi456"]).unwrap();
104
105 let matched = match_(&a, r"\d+").unwrap();
106 let matched_data = matched.as_slice().unwrap();
107 assert_eq!(matched_data, &[true, false, true]);
108
109 let extracted = extract(&a, r"(\d+)").unwrap();
110 assert_eq!(extracted.as_slice(), &["123", "", "456"]);
111 }
112
113 #[test]
114 fn full_pipeline() {
115 let raw = array(&[" Hello ", " World "]).unwrap();
117 let stripped = strip(&raw, None).unwrap();
118 let uppered = upper(&stripped).unwrap();
119 let suffix = array(&["!"]).unwrap();
120 let result = add(&uppered, &suffix).unwrap();
121 assert_eq!(result.as_slice(), &["HELLO!", "WORLD!"]);
122
123 let has_excl = endswith(&result, "!").unwrap();
124 let data = has_excl.as_slice().unwrap();
125 assert_eq!(data, &[true, true]);
126 }
127
128 #[test]
129 fn case_round_trip() {
130 let a = array(&["Hello World"]).unwrap();
131 let low = lower(&a).unwrap();
132 let titled = title(&low).unwrap();
133 assert_eq!(titled.as_slice(), &["Hello World"]);
134 }
135
136 #[test]
137 fn alignment_operations() {
138 let a = array(&["hi"]).unwrap();
139 let c = center(&a, 6, '-').unwrap();
140 assert_eq!(c.as_slice(), &["--hi--"]);
141
142 let l = ljust(&a, 6).unwrap();
143 assert_eq!(l.as_slice(), &["hi "]);
144
145 let r = rjust(&a, 6).unwrap();
146 assert_eq!(r.as_slice(), &[" hi"]);
147
148 let z = zfill(&array(&["42"]).unwrap(), 5).unwrap();
149 assert_eq!(z.as_slice(), &["00042"]);
150 }
151
152 #[test]
153 fn strip_operations() {
154 let a = array(&[" hello "]).unwrap();
155 assert_eq!(strip(&a, None).unwrap().as_slice(), &["hello"]);
156 assert_eq!(lstrip(&a, None).unwrap().as_slice(), &["hello "]);
157 assert_eq!(rstrip(&a, None).unwrap().as_slice(), &[" hello"]);
158 }
159
160 #[test]
161 fn search_operations() {
162 let a = array(&["hello world", "foo bar"]).unwrap();
163 let c = count(&a, "o").unwrap();
164 let data = c.as_slice().unwrap();
165 assert_eq!(data, &[2_u64, 2]);
167 }
168
169 #[test]
170 fn replace_operation() {
171 let a = array(&["hello world"]).unwrap();
172 let b = replace(&a, "world", "rust", None).unwrap();
173 assert_eq!(b.as_slice(), &["hello rust"]);
174 }
175
176 #[test]
177 fn multiply_operation() {
178 let a = array(&["ab"]).unwrap();
179 let b = multiply(&a, 3).unwrap();
180 assert_eq!(b.as_slice(), &["ababab"]);
181 }
182
183 #[test]
184 fn join_operation() {
185 let parts = vec![
186 vec!["a".to_string(), "b".to_string()],
187 vec!["c".to_string(), "d".to_string()],
188 ];
189 let result = join("-", &parts).unwrap();
190 assert_eq!(result.as_slice(), &["a-b", "c-d"]);
191 }
192
193 #[test]
194 fn capitalize_operation() {
195 let a = array(&["hello world", "RUST"]).unwrap();
196 let b = capitalize(&a).unwrap();
197 assert_eq!(b.as_slice(), &["Hello world", "Rust"]);
198 }
199
200 #[test]
201 fn string_array_2d() {
202 let a = StringArray2::from_rows(&[&["a", "b"], &["c", "d"]]).unwrap();
203 assert_eq!(a.shape(), &[2, 2]);
204 let b = upper(&a).unwrap();
205 assert_eq!(b.as_slice(), &["A", "B", "C", "D"]);
206 assert_eq!(b.shape(), &[2, 2]);
207 }
208
209 fn two_by_two(vals: &[&str; 4]) -> crate::StringArray2 {
219 crate::StringArray2::from_rows(&[&[vals[0], vals[1]], &[vals[2], vals[3]]]).unwrap()
220 }
221
222 #[test]
223 fn shape_preserved_case_ops_2d() {
224 let a = two_by_two(&["Hello", "World", "foo", "Bar"]);
225 assert_eq!(upper(&a).unwrap().shape(), &[2, 2]);
226 assert_eq!(lower(&a).unwrap().shape(), &[2, 2]);
227 assert_eq!(capitalize(&a).unwrap().shape(), &[2, 2]);
228 assert_eq!(title(&a).unwrap().shape(), &[2, 2]);
229 }
230
231 #[test]
232 fn shape_preserved_align_ops_2d() {
233 let a = two_by_two(&["a", "bb", "ccc", "dddd"]);
234 assert_eq!(center(&a, 6, ' ').unwrap().shape(), &[2, 2]);
235 assert_eq!(ljust(&a, 6).unwrap().shape(), &[2, 2]);
236 assert_eq!(rjust(&a, 6).unwrap().shape(), &[2, 2]);
237 assert_eq!(zfill(&a, 6).unwrap().shape(), &[2, 2]);
238 }
239
240 #[test]
241 fn shape_preserved_strip_ops_2d() {
242 let a = two_by_two(&[" a ", " b ", " c ", " d "]);
243 assert_eq!(strip(&a, None).unwrap().shape(), &[2, 2]);
244 assert_eq!(lstrip(&a, None).unwrap().shape(), &[2, 2]);
245 assert_eq!(rstrip(&a, None).unwrap().shape(), &[2, 2]);
246 }
247
248 #[test]
249 fn shape_preserved_concat_ops_2d() {
250 let a = two_by_two(&["ab", "cd", "ef", "gh"]);
251 let b = two_by_two(&["!", "!", "!", "!"]);
255 let ab = add(&a, &b).unwrap();
256 assert_eq!(ab.shape(), &[2, 2]);
257 assert_eq!(multiply(&a, 2).unwrap().shape(), &[2, 2]);
259 }
260
261 #[test]
262 fn shape_preserved_search_ops_2d() {
263 let a = two_by_two(&["hello", "help", "world", "word"]);
264 assert_eq!(find(&a, "ell").unwrap().shape(), &[2, 2]);
266 assert_eq!(count(&a, "l").unwrap().shape(), &[2, 2]);
267 assert_eq!(startswith(&a, "he").unwrap().shape(), &[2, 2]);
268 assert_eq!(endswith(&a, "d").unwrap().shape(), &[2, 2]);
269 assert_eq!(replace(&a, "l", "L", None).unwrap().shape(), &[2, 2]);
270 }
271
272 #[test]
273 fn shape_preserved_regex_ops_2d() {
274 let a = two_by_two(&["abc123", "x", "y42", "zzz"]);
275 assert_eq!(match_(&a, r"\d+").unwrap().shape(), &[2, 2]);
277 }
278
279 #[test]
280 fn shape_preserved_case_ops_3d() {
281 use ferray_core::dimension::Ix3;
283 let data: Vec<String> = (0..8).map(|i| format!("s{i}")).collect();
284 let a = crate::StringArray::<Ix3>::from_vec(Ix3::new([2, 2, 2]), data).unwrap();
285 assert_eq!(upper(&a).unwrap().shape(), &[2, 2, 2]);
286 assert_eq!(lower(&a).unwrap().shape(), &[2, 2, 2]);
287 }
288
289 #[test]
292 fn unicode_upper_lower() {
293 let a = array(&["café", "naïve", "über"]).unwrap();
294 let u = upper(&a).unwrap();
295 assert_eq!(u.as_slice(), &["CAFÉ", "NAÏVE", "ÜBER"]);
296 let l = lower(&u).unwrap();
297 assert_eq!(l.as_slice(), &["café", "naïve", "über"]);
298 }
299
300 #[test]
301 fn unicode_capitalize() {
302 let a = array(&["ñoño", "straße"]).unwrap();
303 let c = capitalize(&a).unwrap();
304 assert_eq!(c.as_slice()[0], "Ñoño");
305 assert_eq!(c.as_slice()[1], "Straße");
307 }
308
309 #[test]
310 fn unicode_find() {
311 let a = array(&["日本語テスト", "こんにちは"]).unwrap();
312 let r = find(&a, "テスト").unwrap();
313 let data = r.as_slice().unwrap();
314 assert_eq!(data[0], 3); assert!(data[0] >= 0); assert_eq!(data[1], -1); }
322
323 #[test]
324 fn unicode_strip() {
325 let a = array(&[" héllo ", " wörld "]).unwrap();
326 let s = strip(&a, None).unwrap();
327 assert_eq!(s.as_slice(), &["héllo", "wörld"]);
328 }
329
330 #[test]
331 fn unicode_replace() {
332 let a = array(&["café latte"]).unwrap();
333 let r = replace(&a, "café", "tea", None).unwrap();
334 assert_eq!(r.as_slice(), &["tea latte"]);
335 }
336
337 #[test]
338 fn emoji_operations() {
339 let a = array(&["hello 🌍", "rust 🦀"]).unwrap();
340 let u = upper(&a).unwrap();
341 assert_eq!(u.as_slice(), &["HELLO 🌍", "RUST 🦀"]);
342 let c = count(&a, "🌍").unwrap();
343 assert_eq!(c.as_slice().unwrap(), &[1, 0]);
344 }
345
346 #[test]
347 fn cjk_characters() {
348 let a = array(&["你好世界", "こんにちは"]).unwrap();
349 let starts = startswith(&a, "你好").unwrap();
350 assert_eq!(starts.as_slice().unwrap(), &[true, false]);
351 let ends = endswith(&a, "世界").unwrap();
352 assert_eq!(ends.as_slice().unwrap(), &[true, false]);
353 }
354
355 #[test]
358 fn empty_array_upper() {
359 let a = StringArray1::from_vec(ferray_core::dimension::Ix1::new([0]), vec![]).unwrap();
360 let u = upper(&a).unwrap();
361 assert_eq!(u.len(), 0);
362 }
363
364 #[test]
365 fn empty_array_str_len() {
366 let a = StringArray1::from_vec(ferray_core::dimension::Ix1::new([0]), vec![]).unwrap();
367 let l = str_len(&a).unwrap();
368 assert_eq!(l.size(), 0);
369 }
370
371 #[test]
372 fn empty_array_find() {
373 let a = StringArray1::from_vec(ferray_core::dimension::Ix1::new([0]), vec![]).unwrap();
374 let f = find(&a, "x").unwrap();
375 assert_eq!(f.size(), 0);
376 }
377}