use alloc::{
boxed::Box,
vec::Vec,
};
pub trait TrimSlice {
fn trim(&self) -> &[u8];
fn trim_start(&self) -> &[u8];
fn trim_end(&self) -> &[u8];
}
pub trait TrimSliceMatches {
fn trim_matches<F>(&self, cb: F) -> &[u8]
where F: Fn(u8) -> bool;
fn trim_start_matches<F>(&self, cb: F) -> &[u8]
where F: Fn(u8) -> bool;
fn trim_end_matches<F>(&self, cb: F) -> &[u8]
where F: Fn(u8) -> bool;
}
macro_rules! trim_slice {
($($ty:ty),+ $(,)?) => ($(
impl TrimSlice for $ty {
#[inline]
fn trim(&self) -> &[u8] {
self.trim_matches(|b| b.is_ascii_whitespace())
}
#[inline]
fn trim_start(&self) -> &[u8] {
self.trim_start_matches(|b| b.is_ascii_whitespace())
}
#[inline]
fn trim_end(&self) -> &[u8] {
self.trim_end_matches(|b| b.is_ascii_whitespace())
}
}
impl TrimSliceMatches for $ty {
fn trim_matches<F>(&self, cb: F) -> &[u8]
where F: Fn(u8) -> bool {
let mut src: &[u8] = &self;
while let [first, rest @ ..] = src {
if cb(*first) { src = rest; }
else { break; }
}
while let [rest @ .., last] = src {
if cb(*last) { src = rest; }
else { break; }
}
src
}
fn trim_start_matches<F>(&self, cb: F) -> &[u8]
where F: Fn(u8) -> bool {
let mut src: &[u8] = &self;
while let [first, rest @ ..] = src {
if cb(*first) { src = rest; }
else { break; }
}
src
}
fn trim_end_matches<F>(&self, cb: F) -> &[u8]
where F: Fn(u8) -> bool {
let mut src: &[u8] = &self;
while let [rest @ .., last] = src {
if cb(*last) { src = rest; }
else { break; }
}
src
}
}
)+);
}
trim_slice!([u8], Box<[u8]>, Vec<u8>);
#[cfg(test)]
mod tests {
use super::*;
use brunch as _;
const T_EMPTY: &[u8] = b"";
const T_HELLO: &[u8] = b"hello";
const T_HELLO_E: &[u8] = b"hello\t";
#[test]
fn t_trim() {
let tests: [(&str, &str); 6] = [
("", ""),
(" \t\n\r", ""),
("hello", "hello"),
("hello\t", "hello"),
("\thello", "hello"),
("\n hello world! \t", "hello world!"),
];
for (raw, expected) in tests.iter() {
let a = raw.as_bytes();
let b = expected.as_bytes();
assert_eq!(a.trim(), b);
let a = a.to_vec();
assert_eq!(a.trim(), b);
let a = a.into_boxed_slice();
assert_eq!(a.trim(), b);
}
assert_eq!(T_EMPTY.trim_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(T_EMPTY.to_vec().trim_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(Box::<[u8]>::from(T_EMPTY).trim_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(" ".as_bytes().trim_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(" ".as_bytes().to_vec().trim_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(Box::<[u8]>::from(" ".as_bytes()).trim_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(T_HELLO_E.trim_matches(|b| b'h' == b), b"ello\t");
assert_eq!(T_HELLO_E.to_vec().trim_matches(|b| b'h' == b), b"ello\t");
assert_eq!(Box::<[u8]>::from(T_HELLO_E).trim_matches(|b| b'h' == b), b"ello\t");
let arr: [u8; 5] = [b' ', b' ', b'.', b' ', b' '];
assert_eq!(arr.trim(), &[b'.']);
}
#[test]
fn t_trim_start() {
let tests: [(&str, &str); 6] = [
("", ""),
(" \t\n\r", ""),
("hello", "hello"),
("hello\t", "hello\t"),
("\thello", "hello"),
("\n hello world! \t", "hello world! \t"),
];
for (raw, expected) in tests.iter() {
let a = raw.as_bytes();
let b = expected.as_bytes();
assert_eq!(a.trim_start(), b);
let a = a.to_vec();
assert_eq!(a.trim_start(), b);
let a = a.into_boxed_slice();
assert_eq!(a.trim_start(), b);
}
assert_eq!(T_EMPTY.trim_start_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(T_EMPTY.to_vec().trim_start_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(Box::<[u8]>::from(T_EMPTY).trim_start_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(" ".as_bytes().trim_start_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(" ".as_bytes().to_vec().trim_start_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(Box::<[u8]>::from(" ".as_bytes()).trim_start_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(T_HELLO_E.trim_start_matches(|b| b'h' == b), b"ello\t");
assert_eq!(Box::<[u8]>::from(T_HELLO_E).trim_start_matches(|b| b'h' == b), b"ello\t");
assert_eq!(T_HELLO_E.to_vec().trim_start_matches(|b| b'h' == b), b"ello\t");
}
#[test]
fn t_trim_end() {
let tests: [(&str, &str); 6] = [
("", ""),
(" \t\n\r", ""),
("hello", "hello"),
("hello\t", "hello"),
("\thello", "\thello"),
("\n hello world! \t", "\n hello world!"),
];
for (raw, expected) in tests.iter() {
let a = raw.as_bytes();
let b = expected.as_bytes();
assert_eq!(a.trim_end(), b);
let a = a.to_vec();
assert_eq!(a.trim_end(), b);
let a = a.into_boxed_slice();
assert_eq!(a.trim_end(), b);
}
assert_eq!(T_EMPTY.trim_end_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(T_EMPTY.to_vec().trim_end_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(Box::<[u8]>::from(T_EMPTY).trim_end_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(" ".as_bytes().trim_end_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(" ".as_bytes().to_vec().trim_end_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(Box::<[u8]>::from(" ".as_bytes()).trim_end_matches(|b| b.is_ascii_whitespace()), T_EMPTY);
assert_eq!(T_HELLO_E.trim_matches(|b| b'\t' == b), T_HELLO);
assert_eq!(Box::<[u8]>::from(T_HELLO_E).trim_matches(|b| b'\t' == b), T_HELLO);
assert_eq!(T_HELLO_E.to_vec().trim_matches(|b| b'\t' == b), T_HELLO);
}
}