use std::fmt::{ self, Debug, Formatter };
pub struct Splitter<'a, T: PartialEq> {
data: &'a[T],
pat: &'a[T],
remaining: usize
}
impl<'a, T: PartialEq> Iterator for Splitter<'a, T> {
type Item = &'a[T];
fn next(&mut self) -> Option<&'a[T]> {
macro_rules! take {
(next: $len:expr) => ({
let (slice, remaining) = self.data.split_at($len);
self.data = remaining.split_at(self.pat.len()).1;
self.remaining -= 1;
slice
});
(last) => ({
self.remaining = 0;
self.data
});
}
match self.remaining {
0 => None,
1 => Some(take!(last)),
_ => {
let next_pat = (0 .. self.data.len())
.find(|i| self.data[*i..]
.starts_with(self.pat));
Some(match next_pat {
Some(next_pat) => take!(next: next_pat),
None => take!(last)
})
}
}
}
}
impl<'a, T: PartialEq + Debug> Debug for Splitter<'a, T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("Splitter")
.field("data", &self.data)
.field("pat", &self.pat)
.field("remaining", &self.remaining)
.finish()
}
}
pub trait SliceExt<'a, T: PartialEq> {
fn find(&'a self, pat: &'a dyn AsRef<[T]>) -> Option<usize>;
fn split_pat(&'a self, pat: &'a dyn AsRef<[T]>) -> Splitter<'a, T>;
fn splitn_pat(&'a self, n: usize, pat: &'a dyn AsRef<[T]>) -> Splitter<'a, T>;
}
impl<'a, T: PartialEq + Clone> SliceExt<'a, T> for [T] {
fn find(&'a self, pat: &'a dyn AsRef<[T]>) -> Option<usize> {
let pat = pat.as_ref();
let end = self.len().checked_sub(pat.len())?;
(0 ..= end).find(|i| &self[*i .. *i + pat.len()] == pat)
}
fn split_pat(&'a self, pat: &'a dyn AsRef<[T]>) -> Splitter<'a, T> {
self.splitn_pat(usize::max_value(), pat)
}
fn splitn_pat(&'a self, n: usize, pat: &'a dyn AsRef<[T]>) -> Splitter<'a, T> {
Splitter{ data: self, pat: pat.as_ref(), remaining: n }
}
}
pub trait ByteSliceExt<'a> {
fn trim_start_matches(&'a self, pat: impl Fn(&u8) -> bool) -> &'a Self;
fn trim_end_matches(&'a self, pat: impl Fn(&u8) -> bool) -> &'a Self;
fn trim_matches(&'a self, pat: impl Fn(&u8) -> bool) -> &'a Self {
self.trim_start_matches(&pat).trim_end_matches(&pat)
}
fn trim_start(&'a self) -> &'a Self {
self.trim_start_matches(u8::is_ascii_whitespace)
}
fn trim_end(&'a self) -> &'a Self {
self.trim_end_matches(u8::is_ascii_whitespace)
}
fn trim(&'a self) -> &'a Self {
self.trim_matches(u8::is_ascii_whitespace)
}
}
impl<'a> ByteSliceExt<'a> for [u8] {
fn trim_start_matches(&'a self, pat: impl Fn(&u8) -> bool) -> &'a Self {
let len = self.iter().cloned().take_while(pat).count();
self.split_at(len).1
}
fn trim_end_matches(&'a self, pat: impl Fn(&u8) -> bool) -> &'a Self {
let len = self.iter().cloned().rev().take_while(pat).count();
self.split_at(self.len() - len).0
}
}