use core::fmt;
use core::iter::FusedIterator;
use core::ops::Index;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StrIndex(usize);
impl StrIndex {
pub const fn zero() -> Self {
Self(0)
}
pub const fn byte_index(self) -> usize {
self.0
}
pub const fn advance(&mut self, c: char) {
self.0 += c.len_utf8();
}
pub fn get(self, s: &str) -> Option<char> {
let s = s.get(self.0..)?;
s.chars().next()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StrRange {
pub start: StrIndex,
pub end: StrIndex,
}
impl StrRange {
pub fn get(self, s: &str) -> Option<&str> {
let start = self.start.byte_index();
let end = self.end.byte_index();
s.get(start..end)
}
}
impl Index<StrRange> for str {
type Output = Self;
fn index(&self, index: StrRange) -> &Self::Output {
let start = index.start.byte_index();
let end = index.end.byte_index();
&self[start..end]
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum Quote {
Single,
Double,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum Char {
Whitespace,
Letter(char),
Quote(Quote),
}
impl fmt::Debug for Char {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Whitespace => write!(f, "\" \""),
Self::Letter(c) => write!(f, "\"{c}\""),
Self::Quote(Quote::Single) => write!(f, "\"'\""),
Self::Quote(Quote::Double) => write!(f, "\"\"\""),
}
}
}
impl From<char> for Char {
fn from(c: char) -> Self {
if c.is_whitespace() {
Self::Whitespace
} else if c == '\'' {
Self::Quote(Quote::Single)
} else if c == '\"' {
Self::Quote(Quote::Double)
} else {
Self::Letter(c)
}
}
}
#[derive(Debug, Clone)]
pub struct StrChars<'a> {
s: &'a str,
pos: StrIndex,
}
impl<'a> StrChars<'a> {
pub const fn new(s: &'a str) -> Self {
Self {
s,
pos: StrIndex::zero(),
}
}
pub const fn get(&self) -> &'a str {
self.s
}
pub const fn pos(&self) -> StrIndex {
self.pos
}
pub fn peek(&self) -> Option<Char> {
self.pos.get(self.s).map(Char::from)
}
pub fn advance(&mut self) {
if let Some(c) = self.pos.get(self.s) {
self.pos.advance(c);
}
}
}
impl Iterator for StrChars<'_> {
type Item = Char;
fn next(&mut self) -> Option<Self::Item> {
let c = self.peek()?;
self.advance();
Some(c)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let rem = self
.s
.len()
.checked_sub(self.pos.byte_index())
.expect("pos points past the end of the string");
(0, Some(rem))
}
}
impl FusedIterator for StrChars<'_> {}