use ferray_core::dimension::Dimension;
use ferray_core::error::FerrayResult;
use crate::string_array::StringArray;
fn char_set_predicate(chars: &str) -> impl Fn(char) -> bool + '_ {
let char_set: Vec<char> = chars.chars().collect();
move |c: char| char_set.contains(&c)
}
fn is_python_strip_whitespace(c: char) -> bool {
c.is_whitespace() || matches!(c, '\u{1C}'..='\u{1F}')
}
pub fn strip<D: Dimension>(
a: &StringArray<D>,
chars: Option<&str>,
) -> FerrayResult<StringArray<D>> {
match chars {
None => a.map(|s| s.trim_matches(is_python_strip_whitespace).to_string()),
Some(ch) => {
let pred = char_set_predicate(ch);
a.map(|s| s.trim_matches(&pred).to_string())
}
}
}
pub fn lstrip<D: Dimension>(
a: &StringArray<D>,
chars: Option<&str>,
) -> FerrayResult<StringArray<D>> {
match chars {
None => a.map(|s| s.trim_start_matches(is_python_strip_whitespace).to_string()),
Some(ch) => {
let pred = char_set_predicate(ch);
a.map(|s| s.trim_start_matches(&pred).to_string())
}
}
}
pub fn rstrip<D: Dimension>(
a: &StringArray<D>,
chars: Option<&str>,
) -> FerrayResult<StringArray<D>> {
match chars {
None => a.map(|s| s.trim_end_matches(is_python_strip_whitespace).to_string()),
Some(ch) => {
let pred = char_set_predicate(ch);
a.map(|s| s.trim_end_matches(&pred).to_string())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::string_array::array;
#[test]
fn test_strip_whitespace() {
let a = array(&[" hello ", "\tworld\n"]).unwrap();
let b = strip(&a, None).unwrap();
assert_eq!(b.as_slice(), &["hello", "world"]);
}
#[test]
fn test_strip_chars() {
let a = array(&["xxhelloxx", "xyworldyx"]).unwrap();
let b = strip(&a, Some("xy")).unwrap();
assert_eq!(b.as_slice(), &["hello", "world"]);
}
#[test]
fn test_lstrip_whitespace() {
let a = array(&[" hello ", "\tworld\n"]).unwrap();
let b = lstrip(&a, None).unwrap();
assert_eq!(b.as_slice(), &["hello ", "world\n"]);
}
#[test]
fn test_lstrip_chars() {
let a = array(&["xxhello", "xyhello"]).unwrap();
let b = lstrip(&a, Some("xy")).unwrap();
assert_eq!(b.as_slice(), &["hello", "hello"]);
}
#[test]
fn test_rstrip_whitespace() {
let a = array(&[" hello ", "\tworld\n"]).unwrap();
let b = rstrip(&a, None).unwrap();
assert_eq!(b.as_slice(), &[" hello", "\tworld"]);
}
#[test]
fn test_rstrip_chars() {
let a = array(&["helloxx", "worldyx"]).unwrap();
let b = rstrip(&a, Some("xy")).unwrap();
assert_eq!(b.as_slice(), &["hello", "world"]);
}
#[test]
fn test_strip_empty_string() {
let a = array(&["", " "]).unwrap();
let b = strip(&a, None).unwrap();
assert_eq!(b.as_slice(), &["", ""]);
}
#[test]
fn test_strip_c0_separators() {
for ch in ['\u{1C}', '\u{1D}', '\u{1E}', '\u{1F}'] {
let s = format!("{ch}hi{ch}");
let a = array(&[s.as_str()]).unwrap();
let b = strip(&a, None).unwrap();
assert_eq!(
b.as_slice(),
&["hi"],
"strip failed for U+{:04X}",
ch as u32
);
}
}
#[test]
fn test_lstrip_c0_separators() {
for ch in ['\u{1C}', '\u{1D}', '\u{1E}', '\u{1F}'] {
let s = format!("{ch}ab");
let a = array(&[s.as_str()]).unwrap();
let b = lstrip(&a, None).unwrap();
assert_eq!(
b.as_slice(),
&["ab"],
"lstrip failed for U+{:04X}",
ch as u32
);
}
}
#[test]
fn test_rstrip_c0_separators() {
for ch in ['\u{1C}', '\u{1D}', '\u{1E}', '\u{1F}'] {
let s = format!("ab{ch}");
let a = array(&[s.as_str()]).unwrap();
let b = rstrip(&a, None).unwrap();
assert_eq!(
b.as_slice(),
&["ab"],
"rstrip failed for U+{:04X}",
ch as u32
);
}
}
#[test]
fn test_strip_unicode_whitespace_still_works() {
let a = array(&["\u{85}\u{A0}\u{0B}\u{0C} hi \t\n\r "]).unwrap();
let b = strip(&a, None).unwrap();
assert_eq!(b.as_slice(), &["hi"]);
}
}