use alloc::{
borrow::Cow,
boxed::Box,
string::String,
vec::Vec,
};
use crate::{
pattern::MatchPattern,
TrimSliceMatches,
};
pub trait TrimMut {
fn trim_mut(&mut self);
fn trim_start_mut(&mut self);
fn trim_end_mut(&mut self);
}
pub trait TrimMatchesMut {
type MatchUnit: Copy + Eq + Ord + Sized;
fn trim_matches_mut<P: MatchPattern<Self::MatchUnit>>(&mut self, pat: P);
fn trim_start_matches_mut<P: MatchPattern<Self::MatchUnit>>(&mut self, pat: P);
fn trim_end_matches_mut<P: MatchPattern<Self::MatchUnit>>(&mut self, pat: P);
}
impl TrimMut for String {
#[inline]
fn trim_mut(&mut self) {
self.trim_end_matches_mut(char::is_whitespace);
self.trim_start_matches_mut(char::is_whitespace);
}
#[inline]
fn trim_start_mut(&mut self) {
self.trim_start_matches_mut(char::is_whitespace);
}
#[inline]
fn trim_end_mut(&mut self) {
self.trim_end_matches_mut(char::is_whitespace);
}
}
impl TrimMatchesMut for String {
type MatchUnit = char;
#[inline]
fn trim_matches_mut<P: MatchPattern<char>>(&mut self, pat: P) {
self.trim_end_matches_mut(pat);
self.trim_start_matches_mut(pat);
}
#[inline]
fn trim_start_matches_mut<P: MatchPattern<char>>(&mut self, pat: P) {
if let Some(start) = self.find(#[inline(always)] |c| ! pat.is_match(c)) {
if start != 0 { self.replace_range(..start, ""); }
}
else { self.truncate(0); }
}
#[inline]
fn trim_end_matches_mut<P: MatchPattern<char>>(&mut self, pat: P) {
let trimmed_len = self.trim_end_matches(#[inline(always)] |c| pat.is_match(c)).len();
self.truncate(trimmed_len);
}
}
impl TrimMut for Cow<'_, str> {
#[inline]
fn trim_mut(&mut self) {
match self {
Cow::Borrowed(s) => { *self = Cow::Borrowed(s.trim()); },
Cow::Owned(s) => { s.trim_mut(); },
}
}
#[inline]
fn trim_start_mut(&mut self) {
match self {
Cow::Borrowed(s) => { *self = Cow::Borrowed(s.trim_start()); },
Cow::Owned(s) => { s.trim_start_mut(); },
}
}
#[inline]
fn trim_end_mut(&mut self) {
match self {
Cow::Borrowed(s) => { *self = Cow::Borrowed(s.trim_end()); },
Cow::Owned(s) => { s.trim_end_mut(); },
}
}
}
impl TrimMatchesMut for Cow<'_, str> {
type MatchUnit = char;
#[inline]
fn trim_matches_mut<P: MatchPattern<char>>(&mut self, pat: P) {
match self {
Cow::Borrowed(s) => {
*self = Cow::Borrowed(s.trim_matches(#[inline(always)] |c| pat.is_match(c)));
},
Cow::Owned(s) => { s.trim_matches_mut(pat); },
}
}
#[inline]
fn trim_start_matches_mut<P: MatchPattern<char>>(&mut self, pat: P) {
match self {
Cow::Borrowed(s) => {
*self = Cow::Borrowed(s.trim_start_matches(#[inline(always)] |c| pat.is_match(c)));
},
Cow::Owned(s) => { s.trim_start_matches_mut(pat); },
}
}
#[inline]
fn trim_end_matches_mut<P: MatchPattern<char>>(&mut self, pat: P) {
match self {
Cow::Borrowed(s) => {
*self = Cow::Borrowed(s.trim_end_matches(#[inline(always)] |c| pat.is_match(c)));
},
Cow::Owned(s) => { s.trim_end_matches_mut(pat); },
}
}
}
impl TrimMut for Box<[u8]> {
#[inline]
fn trim_mut(&mut self) {
let trimmed = self.trim_ascii();
if trimmed.len() < self.len() { *self = Self::from(trimmed); }
}
#[inline]
fn trim_start_mut(&mut self) {
let trimmed = self.trim_ascii_start();
if trimmed.len() < self.len() { *self = Self::from(trimmed); }
}
#[inline]
fn trim_end_mut(&mut self) {
let trimmed = self.trim_ascii_end();
if trimmed.len() < self.len() { *self = Self::from(trimmed); }
}
}
impl TrimMatchesMut for Box<[u8]> {
type MatchUnit = u8;
#[inline]
fn trim_matches_mut<P: MatchPattern<u8>>(&mut self, pat: P) {
let trimmed = self.trim_matches(pat);
if trimmed.len() < self.len() { *self = Self::from(trimmed); }
}
#[inline]
fn trim_start_matches_mut<P: MatchPattern<u8>>(&mut self, pat: P) {
let trimmed = self.trim_start_matches(pat);
if trimmed.len() < self.len() { *self = Self::from(trimmed); }
}
#[inline]
fn trim_end_matches_mut<P: MatchPattern<u8>>(&mut self, pat: P) {
let trimmed = self.trim_end_matches(pat);
if trimmed.len() < self.len() { *self = Self::from(trimmed); }
}
}
impl TrimMut for Vec<u8> {
#[inline]
fn trim_mut(&mut self) {
self.trim_end_mut();
self.trim_start_mut();
}
#[inline]
fn trim_start_mut(&mut self) {
let slice: &[u8] = self.as_slice();
let before = slice.len();
let after = slice.trim_ascii_start().len();
if after < before {
if after != 0 { self.copy_within(before - after.., 0); }
self.truncate(after);
}
}
#[inline]
fn trim_end_mut(&mut self) {
let trimmed_len = self.trim_ascii_end().len();
self.truncate(trimmed_len);
}
}
impl TrimMatchesMut for Vec<u8> {
type MatchUnit = u8;
#[inline]
fn trim_matches_mut<P: MatchPattern<u8>>(&mut self, pat: P) {
self.trim_end_matches_mut(pat);
self.trim_start_matches_mut(pat);
}
#[inline]
fn trim_start_matches_mut<P: MatchPattern<u8>>(&mut self, pat: P) {
if let Some(start) = self.iter().copied().position(#[inline(always)] |b| ! pat.is_match(b)) {
if 0 != start {
let trimmed_len = self.len() - start;
self.copy_within(start.., 0);
self.truncate(trimmed_len);
}
}
else { self.truncate(0); }
}
#[inline]
fn trim_end_matches_mut<P: MatchPattern<u8>>(&mut self, pat: P) {
let end = self.iter()
.copied()
.rposition(#[inline(always)] |b| ! pat.is_match(b))
.map_or(0, |e| e + 1);
self.truncate(end);
}
}
impl TrimMut for Cow<'_, [u8]> {
#[inline]
fn trim_mut(&mut self) {
match self {
Cow::Borrowed(s) => { *self = Cow::Borrowed(s.trim_ascii()); },
Cow::Owned(s) => { s.trim_mut(); },
}
}
#[inline]
fn trim_start_mut(&mut self) {
match self {
Cow::Borrowed(s) => { *self = Cow::Borrowed(s.trim_ascii_start()); },
Cow::Owned(s) => { s.trim_start_mut(); },
}
}
#[inline]
fn trim_end_mut(&mut self) {
match self {
Cow::Borrowed(s) => { *self = Cow::Borrowed(s.trim_ascii_end()); },
Cow::Owned(s) => { s.trim_end_mut(); },
}
}
}
impl TrimMatchesMut for Cow<'_, [u8]> {
type MatchUnit = u8;
#[inline]
fn trim_matches_mut<P: MatchPattern<u8>>(&mut self, pat: P) {
match self {
Cow::Borrowed(s) => {
*self = Cow::Borrowed(s.trim_matches(pat));
},
Cow::Owned(s) => { s.trim_matches_mut(pat); },
}
}
#[inline]
fn trim_start_matches_mut<P: MatchPattern<u8>>(&mut self, pat: P) {
match self {
Cow::Borrowed(s) => {
*self = Cow::Borrowed(s.trim_start_matches(pat));
},
Cow::Owned(s) => { s.trim_start_matches_mut(pat); },
}
}
#[inline]
fn trim_end_matches_mut<P: MatchPattern<u8>>(&mut self, pat: P) {
match self {
Cow::Borrowed(s) => {
*self = Cow::Borrowed(s.trim_end_matches(pat));
},
Cow::Owned(s) => { s.trim_end_matches_mut(pat); },
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn trim_str() {
use alloc::borrow::ToOwned;
for v in [
"ĤéĹlo the WŎrld\u{0300}",
" ĤéĹlo the WŎrld\u{0300}",
" \tĤéĹlo the WŎrld\u{0300}",
"\r \nĤéĹlo\nthe WŎrld\u{0300}",
" ĤéĹlo the WŎrld\u{0300}\u{2003} ",
" \tĤéĹlo the WŎrld\u{0300} ",
"\r \nĤéĹlo\nthe WŎrld\u{0300} \t\t",
"ĤéĹlo the WŎrld\u{0300}\0 ",
"ĤéĹlo the WŎrld\u{0300}\r\r",
"ĤéĹlo the WŎrld\u{0300} \r\t",
"\nHello\nWorld\n!\n",
] {
let mut v2 = v.to_owned();
v2.trim_start_mut();
assert_eq!(v2, v.trim_start());
v.clone_into(&mut v2);
v2.trim_end_mut();
assert_eq!(v2, v.trim_end());
v.clone_into(&mut v2);
v2.trim_mut();
assert_eq!(v2, v.trim());
v.clone_into(&mut v2);
v2.trim_matches_mut(|c| c == '\t');
assert_eq!(v2, v.trim_matches(|c| c == '\t'));
v.clone_into(&mut v2);
v2.trim_matches_mut('\t');
assert_eq!(v2, v.trim_matches(|c| c == '\t'));
v.clone_into(&mut v2);
v2.trim_matches_mut(['\t']);
assert_eq!(v2, v.trim_matches(|c| c == '\t'));
v.clone_into(&mut v2);
v2.trim_matches_mut(&['\t']);
assert_eq!(v2, v.trim_matches(|c| c == '\t'));
}
}
}