use std::iter::{Peekable};
#[derive(Debug, PartialOrd, PartialEq)]
pub(crate) enum Token<'source> {
Text(&'source str),
OpenBracket,
CloseBracket,
}
pub(crate) struct Lexer<'source> {
cursor: Peekable<std::str::CharIndices<'source>>,
source: &'source str,
tokens: Vec<Token<'source>>,
text_start: Option<usize>,
}
impl<'source, T> From<T> for Lexer<'source>
where
T: Into<&'source str>,
{
fn from(value: T) -> Self {
let source: &'source str = value.into();
Self {
source,
cursor: source.char_indices().peekable(),
tokens: Default::default(),
text_start: None,
}
}
}
impl<'source> Lexer<'source> {
pub fn collect(mut self) -> Vec<Token<'source>> {
while let Some((index, char)) = self.cursor.next() {
if char == '{' && matches!(self.cursor.peek(), Some((_, '{'))) {
self.flush(index);
self.cursor.next();
self.tokens.push(Token::OpenBracket);
} else if char == '}'
&& matches!(self.cursor.peek(), Some((_, '}')))
{
self.flush(index);
self.cursor.next();
self.tokens.push(Token::CloseBracket);
} else {
self.text_start.get_or_insert(index);
}
}
self.flush(self.source.len());
self.tokens
}
fn flush(
&mut self,
index: usize,
) {
if let Some(start) = self.text_start {
if start < index {
self.tokens.push(Token::Text(&self.source[start..index]));
}
self.text_start = None;
}
}
}