use std::iter::Peekable;
use crate::id::specificity::tokens::{AsTokens, Token, Tokens};
use super::atom::{Atom, Character, Wildcard};
use super::segments::Segments;
use super::Segment;
pub trait ToSegments {
fn to_segments(&self) -> Segments<'_>;
}
impl<T> ToSegments for T
where
T: AsTokens,
{
#[inline]
fn to_segments(&self) -> Segments<'_> {
parse(&mut self.as_tokens().peekable(), false)
}
}
type Iter<'a> = Peekable<Tokens<'a>>;
fn parse<'a>(iter: &mut Iter<'a>, group: bool) -> Segments<'a> {
let mut segments = Vec::new();
while let Some(token) = iter.peek() {
match token {
Token::Comma | Token::GroupEnd if group => break,
_ => segments.push(parse_segment(iter, group)),
}
}
Segments::from_iter(segments)
}
fn parse_segment<'a>(iter: &mut Iter<'a>, group: bool) -> Segment<'a> {
let mut atoms = Vec::new();
while let Some(token) = iter.peek() {
if group && matches!(token, Token::Comma | Token::GroupEnd) {
break;
}
atoms.push(match iter.next().expect("invariant") {
Token::Any => Atom::Wildcard(Wildcard::Character),
Token::Star => Atom::Wildcard(Wildcard::Sequence),
Token::StarStar => Atom::Wildcard(Wildcard::Traversal),
Token::CharacterStart => Atom::Character(parse_character(iter)),
Token::GroupStart => Atom::Group(parse_group(iter)),
Token::Separator => break,
other => Atom::Literal(other.as_str()),
});
}
Segment::from_iter(atoms)
}
fn parse_character<'a>(iter: &mut Iter<'a>) -> Character<'a> {
let mut values = Vec::new();
for token in iter.by_ref() {
values.push(match token {
Token::CharacterEnd => break,
other => other.as_str(),
});
}
Character::from_iter(values)
}
fn parse_group<'a>(iter: &mut Iter<'a>) -> Vec<Segments<'a>> {
let mut group = Vec::new();
loop {
group.push(parse(iter, true));
match iter.peek() {
Some(Token::Comma) => iter.next(),
Some(Token::GroupEnd) => {
iter.next();
break;
}
_ => break,
};
}
group
}