Skip to main content

ferray_strings/
strip.rs

1// ferray-strings: Stripping operations (REQ-7)
2//
3// Implements strip, lstrip, rstrip — elementwise on StringArray.
4
5use ferray_core::dimension::Dimension;
6use ferray_core::error::FerrayResult;
7
8use crate::string_array::StringArray;
9
10/// Build a char-set predicate from `chars`. Shared by strip/lstrip/rstrip
11/// so the `char_set: Vec<char>` construction lives in one place (#280).
12fn char_set_predicate(chars: &str) -> impl Fn(char) -> bool + '_ {
13    let char_set: Vec<char> = chars.chars().collect();
14    move |c: char| char_set.contains(&c)
15}
16
17/// Strip leading and trailing characters from each string element.
18///
19/// If `chars` is `None`, strips whitespace. Otherwise strips any character
20/// present in the `chars` string.
21///
22/// # Errors
23/// Returns an error if the internal array construction fails.
24pub fn strip<D: Dimension>(
25    a: &StringArray<D>,
26    chars: Option<&str>,
27) -> FerrayResult<StringArray<D>> {
28    match chars {
29        None => a.map(|s| s.trim().to_string()),
30        Some(ch) => {
31            let pred = char_set_predicate(ch);
32            a.map(|s| s.trim_matches(&pred).to_string())
33        }
34    }
35}
36
37/// Strip leading characters from each string element.
38///
39/// If `chars` is `None`, strips leading whitespace. Otherwise strips any
40/// character present in the `chars` string from the left.
41///
42/// # Errors
43/// Returns an error if the internal array construction fails.
44pub fn lstrip<D: Dimension>(
45    a: &StringArray<D>,
46    chars: Option<&str>,
47) -> FerrayResult<StringArray<D>> {
48    match chars {
49        None => a.map(|s| s.trim_start().to_string()),
50        Some(ch) => {
51            let pred = char_set_predicate(ch);
52            a.map(|s| s.trim_start_matches(&pred).to_string())
53        }
54    }
55}
56
57/// Strip trailing characters from each string element.
58///
59/// If `chars` is `None`, strips trailing whitespace. Otherwise strips any
60/// character present in the `chars` string from the right.
61///
62/// # Errors
63/// Returns an error if the internal array construction fails.
64pub fn rstrip<D: Dimension>(
65    a: &StringArray<D>,
66    chars: Option<&str>,
67) -> FerrayResult<StringArray<D>> {
68    match chars {
69        None => a.map(|s| s.trim_end().to_string()),
70        Some(ch) => {
71            let pred = char_set_predicate(ch);
72            a.map(|s| s.trim_end_matches(&pred).to_string())
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80    use crate::string_array::array;
81
82    #[test]
83    fn test_strip_whitespace() {
84        let a = array(&["  hello  ", "\tworld\n"]).unwrap();
85        let b = strip(&a, None).unwrap();
86        assert_eq!(b.as_slice(), &["hello", "world"]);
87    }
88
89    #[test]
90    fn test_strip_chars() {
91        let a = array(&["xxhelloxx", "xyworldyx"]).unwrap();
92        let b = strip(&a, Some("xy")).unwrap();
93        assert_eq!(b.as_slice(), &["hello", "world"]);
94    }
95
96    #[test]
97    fn test_lstrip_whitespace() {
98        let a = array(&["  hello  ", "\tworld\n"]).unwrap();
99        let b = lstrip(&a, None).unwrap();
100        assert_eq!(b.as_slice(), &["hello  ", "world\n"]);
101    }
102
103    #[test]
104    fn test_lstrip_chars() {
105        let a = array(&["xxhello", "xyhello"]).unwrap();
106        let b = lstrip(&a, Some("xy")).unwrap();
107        assert_eq!(b.as_slice(), &["hello", "hello"]);
108    }
109
110    #[test]
111    fn test_rstrip_whitespace() {
112        let a = array(&["  hello  ", "\tworld\n"]).unwrap();
113        let b = rstrip(&a, None).unwrap();
114        assert_eq!(b.as_slice(), &["  hello", "\tworld"]);
115    }
116
117    #[test]
118    fn test_rstrip_chars() {
119        let a = array(&["helloxx", "worldyx"]).unwrap();
120        let b = rstrip(&a, Some("xy")).unwrap();
121        assert_eq!(b.as_slice(), &["hello", "world"]);
122    }
123
124    #[test]
125    fn test_strip_empty_string() {
126        let a = array(&["", "   "]).unwrap();
127        let b = strip(&a, None).unwrap();
128        assert_eq!(b.as_slice(), &["", ""]);
129    }
130}