use super::*;
use libsugar::Also;
use serde::{Deserialize, Serialize};
use std::convert::From;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::string::ToString;
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, PartialOrd, Ord, Deserialize, Serialize)]
pub enum Char {
Char(char, Loc),
Wrap(Loc),
}
impl Char {
#[inline]
pub fn char<R>(&self, mut f: impl FnMut(char, Loc) -> R) -> Option<R> {
match self {
Char::Char(c, l) => Some(f(*c, *l)),
_ => None,
}
}
#[inline]
pub fn wrap<R>(&self, mut f: impl FnMut(Loc) -> R) -> Option<R> {
match self {
Char::Wrap(l) => Some(f(*l)),
_ => None,
}
}
#[inline]
pub fn is_char(&self) -> bool {
matches!(self, Char::Char(_, _))
}
#[inline]
pub fn is_wrap(&self) -> bool {
matches!(self, Char::Wrap(_))
}
#[inline]
pub fn char_eq(&self, other: &Self) -> bool {
match self {
Char::Char(c, _) => {
if let Char::Char(oc, _) = other {
c == oc
} else {
false
}
}
Char::Wrap(_) => matches!(other, Char::Wrap(_)),
}
}
#[inline]
pub fn char_ne(&self, other: &Self) -> bool {
!self.char_eq(other)
}
#[inline]
pub fn c(&self) -> char {
match self {
Char::Char(c, _) => *c,
Char::Wrap(_) => '\n',
}
}
}
impl GetLoc for Char {
#[inline]
fn loc(&self) -> Loc {
match self {
Char::Char(_, l) => *l,
Char::Wrap(l) => *l,
}
}
}
impl Display for Char {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Char::Char(c, _) => write!(f, "Char({})", c),
Char::Wrap(_) => write!(f, "Wrap"),
}
}
}
impl PartialEq<char> for Char {
fn eq(&self, other: &char) -> bool {
match self {
Char::Char(c, _) => *c == *other,
Char::Wrap(_) => '\n' == *other,
}
}
}
impl Into<char> for Char {
#[inline]
fn into(self) -> char {
self.c()
}
}
impl GetString for Char {
#[inline]
fn get_string(&self) -> String {
self.c().to_string()
}
}
impl GetChar for Char {
#[inline]
fn get_char(&self) -> char {
self.c()
}
}
#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq, Default)]
pub struct CharChars<I: Iterator<Item = char>> {
iter: I,
loc: Loc,
isr: Option<Loc>,
end: bool,
nextret: Option<Char>,
}
impl<I: Iterator<Item = char>> CharChars<I> {
pub fn new(iter: I) -> Self {
Self {
iter,
loc: Loc::new(),
isr: None,
end: false,
nextret: None,
}
}
}
impl<I: Iterator<Item = char>> From<I> for CharChars<I> {
#[inline]
fn from(iter: I) -> Self {
Self::new(iter)
}
}
impl<I: Iterator<Item = char>> Iterator for CharChars<I> {
type Item = Char;
fn next(&mut self) -> Option<Self::Item> {
if let Some(r) = self.nextret {
self.nextret = None;
return Some(r);
}
if self.end {
return None;
}
loop {
let n = self.iter.next();
if let Some(c) = n {
if let Some(l) = self.isr {
if c == '\n' {
self.isr = None;
return Some(Char::Wrap(l)).also(|_| {
self.loc.offset += 1;
});
} else {
if c == '\r' {
self.isr = Some(self.loc);
self.loc.offset += 1;
self.loc.char = 0;
self.loc.line += 1;
return Some(Char::Wrap(l));
} else {
self.isr = None;
self.nextret = Some(Char::Char(c, self.loc));
self.loc.offset += 1;
self.loc.char += 1;
return Some(Char::Wrap(l));
}
}
} else {
if c == '\r' {
self.isr = Some(self.loc);
self.loc.offset += 1;
self.loc.char = 0;
self.loc.line += 1;
} else if c == '\n' {
return Some(Char::Wrap(self.loc)).also(|_| {
self.loc.offset += 1;
self.loc.char = 0;
self.loc.line += 1;
});
} else {
return Some(Char::Char(c, self.loc)).also(|_| {
self.loc.offset += 1;
self.loc.char += 1;
});
}
}
} else {
if let Some(l) = self.isr {
self.end = true;
return Some(Char::Wrap(l));
} else {
self.end = true;
return None;
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::{Char, CharChars, Loc};
#[test]
fn test_char_chars() {
let code = "a\nb\rc\r\nd";
let mut cc = CharChars::new(code.chars());
assert_eq!(cc.next(), Some(Char::Char('a', Loc::new_at(0, 0, 0))));
assert_eq!(cc.next(), Some(Char::Wrap(Loc::new_at(1, 0, 1))));
assert_eq!(cc.next(), Some(Char::Char('b', Loc::new_at(2, 1, 0))));
assert_eq!(cc.next(), Some(Char::Wrap(Loc::new_at(3, 1, 1))));
assert_eq!(cc.next(), Some(Char::Char('c', Loc::new_at(4, 2, 0))));
assert_eq!(cc.next(), Some(Char::Wrap(Loc::new_at(5, 2, 1))));
assert_eq!(cc.next(), Some(Char::Char('d', Loc::new_at(7, 3, 0))));
assert_eq!(cc.next(), None);
}
}