use std::str::CharIndices;
pub trait Parse: Sized {
type Err;
fn parse(value: &str) -> Result<Self, Self::Err>;
}
pub trait ExtSplit {
fn split_excluding_group(
&self,
delimiter: char,
group_start: char,
group_end: char,
) -> SplitExcludingGroup<'_>;
fn split_ascii_whitespace_excluding_group(
&self,
group_start: char,
group_end: char,
) -> SplitAsciiWhitespaceExcludingGroup<'_>;
}
impl ExtSplit for str {
fn split_excluding_group(
&self,
delimiter: char,
group_start: char,
group_end: char,
) -> SplitExcludingGroup<'_> {
SplitExcludingGroup {
text: self,
chars: self.char_indices(),
delimiter,
group_start,
group_end,
trailing_empty: true,
}
}
fn split_ascii_whitespace_excluding_group(
&self,
group_start: char,
group_end: char,
) -> SplitAsciiWhitespaceExcludingGroup<'_> {
SplitAsciiWhitespaceExcludingGroup {
text: self,
chars: self.char_indices(),
group_start,
group_end,
}
}
}
#[derive(Clone, Debug)]
pub struct SplitExcludingGroup<'a> {
pub text: &'a str,
pub chars: CharIndices<'a>,
pub delimiter: char,
pub group_start: char,
pub group_end: char,
pub trailing_empty: bool,
}
impl<'a> Iterator for SplitExcludingGroup<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
let first = self.chars.next();
let (start, mut prev) = match first {
None => {
if self.text.ends_with(self.delimiter) && self.trailing_empty {
self.trailing_empty = false;
return Some("");
}
return None;
}
Some((_, c)) if c == self.delimiter => return Some(""),
Some(v) => v,
};
let mut in_group = false;
let mut nesting = -1;
loop {
if prev == self.group_start {
if nesting == -1 {
in_group = true;
}
nesting += 1;
} else if prev == self.group_end {
nesting -= 1;
if nesting == -1 {
in_group = false;
}
}
prev = match self.chars.next() {
None => return Some(&self.text[start..]),
Some((end, c)) if c == self.delimiter && !in_group => {
return Some(&self.text[start..end])
}
Some((_, c)) => c,
}
}
}
}
#[derive(Clone, Debug)]
pub struct SplitAsciiWhitespaceExcludingGroup<'a> {
pub text: &'a str,
pub chars: CharIndices<'a>,
pub group_start: char,
pub group_end: char,
}
impl<'a> Iterator for SplitAsciiWhitespaceExcludingGroup<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
let first = self.chars.next();
let (start, mut prev) = match first {
None => return None,
Some((_, c)) if c.is_ascii_whitespace() => return self.next(),
Some(v) => v,
};
let mut in_group = false;
let mut nesting = -1;
loop {
if prev == self.group_start {
if nesting == -1 {
in_group = true;
}
nesting += 1;
} else if prev == self.group_end {
nesting -= 1;
if nesting == -1 {
in_group = false;
}
}
prev = match self.chars.next() {
None => return Some(&self.text[start..]),
Some((end, c)) if c.is_ascii_whitespace() && !in_group => {
return Some(&self.text[start..end])
}
Some((_, c)) => c,
}
}
}
}