pub struct Scanner { source: Box<[char]>, position: usize, } impl Scanner { pub fn new(source: impl AsRef) -> Self { Self { source: source.as_ref().chars().collect(), position: 0, } } pub fn position(&self) -> usize { self.position } pub fn goto(&mut self, position: usize) -> Option { // allow reverse ranges let production = if self.position < position { self.source.get(self.position..position)?.iter().collect() } else { self.source .get(position..self.position)? .iter() .rev() .collect() }; self.position = position; Some(production) } pub fn advance(&mut self, amount: usize) -> Option { self.goto(self.position + amount) } pub fn find_substring(&self, substring: impl AsRef) -> Option { self.source .get(self.position..)? .iter() .collect::() .find(substring.as_ref()) } pub fn starts_with(&self, substring: impl AsRef) -> Option { let mut i = self.position; for substring_char in substring.as_ref().chars() { if *self.source.get(i)? != substring_char { return None; } i += 1; } Some(i) } }