use std::{borrow::Cow, io::Read};
use reader_source::ReaderSource;
use crate::{ArgError, Result};
mod from_read;
mod parsed_fmt;
mod read_fmt;
mod reader_chars;
mod reader_source;
mod set_from_read;
mod trim_side;
pub use self::{
from_read::*, parsed_fmt::*, read_fmt::*, reader_chars::*,
set_from_read::*, trim_side::*,
};
pub struct Reader<'a> {
source: ReaderSource<'a>,
undone: Vec<char>,
pos: usize,
}
impl<'a> Reader<'a> {
pub fn read_to(&mut self, s: &mut String, max: usize) -> Result<()> {
s.reserve(self.bytes_size_hint().min(max));
let target = s.len().saturating_add(max);
for c in self.chars() {
s.push(c?);
if s.len() == target {
break;
}
}
Ok(())
}
pub fn read_all(&mut self, s: &mut String) -> Result<()> {
s.reserve(self.bytes_size_hint());
for c in self.chars() {
s.push(c?);
}
Ok(())
}
pub fn pos(&self) -> usize {
self.pos
}
pub fn bytes_size_hint(&self) -> usize {
match &self.source {
ReaderSource::Io(_) => self.undone.len(),
ReaderSource::Str(s) => s.len() - self.pos + self.undone.len(),
ReaderSource::Iter(i) => i.size_hint().0 + self.undone.len(),
ReaderSource::IterErr(i) => i.size_hint().0 + self.undone.len(),
}
}
pub fn map_err(&self, e: ArgError) -> ArgError {
match &self.source {
ReaderSource::Str(s) => e
.shift_span(self.pos.saturating_sub(1), s.to_string())
.spanned(self.pos.saturating_sub(1)..self.pos),
_ => e,
}
}
pub fn err_parse(&self, msg: impl Into<Cow<'static, str>>) -> ArgError {
self.map_err(ArgError::failed_to_parse(msg, String::new()))
}
pub fn err_value(&self, msg: impl Into<Cow<'static, str>>) -> ArgError {
self.map_err(ArgError::invalid_value(msg, String::new()))
}
pub fn map_err_peek(&self, e: ArgError) -> ArgError {
match &self.source {
ReaderSource::Str(s) => e
.shift_span(self.pos, s.to_string())
.spanned(self.pos..self.pos),
_ => e,
}
}
pub fn err_parse_peek(
&self,
msg: impl Into<Cow<'static, str>>,
) -> ArgError {
self.map_err_peek(ArgError::failed_to_parse(msg, String::new()))
}
pub fn err_value_peek(
&self,
msg: impl Into<Cow<'static, str>>,
) -> ArgError {
self.map_err(ArgError::invalid_value(msg, String::new()))
}
pub fn peek(&mut self) -> Result<Option<char>> {
if self.undone.is_empty() {
let n = self.next_inner()?;
self.undone.extend(n);
}
Ok(self.undone.last().copied())
}
pub fn expect(&mut self, s: &str) -> Result<()> {
for p in s.chars() {
let Some(s) = self.next()? else {
return self
.err_parse("Unexpected end of string.")
.inline_msg(format!("Expected `{p}` to form `{s}`"))
.err();
};
if p != s {
return self
.err_parse(format!("Unexpected character `{s}`."))
.inline_msg(format!("Expected `{p}` to form `{s}`."))
.err();
}
}
Ok(())
}
pub fn skip_while(
&mut self,
mut f: impl FnMut(char) -> bool,
) -> Result<()> {
while let Some(c) = self.peek()? {
if !f(c) {
break;
}
self.next()?;
}
Ok(())
}
pub fn is_next_some(&mut self, c: char) -> Result<bool> {
if matches!(self.peek()?, Some(v) if v == c) {
self.next()?;
Ok(true)
} else {
Ok(false)
}
}
pub fn is_next(
&mut self,
p: impl FnOnce(Option<char>) -> bool,
) -> Result<bool> {
if p(self.peek()?) {
self.next()?;
Ok(true)
} else {
Ok(false)
}
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<Option<char>> {
let c = if let Some(c) = self.undone.pop() {
c
} else if let Some(c) = self.next_inner()? {
c
} else {
return Ok(None);
};
self.pos += c.len_utf8();
Ok(Some(c))
}
pub fn chars(&mut self) -> ReaderChars<'_, 'a> {
ReaderChars(self)
}
pub fn parse<'f, T: FromRead>(
&mut self,
fmt: &'f ReadFmt<'f>,
) -> Result<(T, Option<ArgError>)> {
T::from_read(self, fmt)
}
pub fn trim_left(&mut self, fmt: &ReadFmt) -> Result<()> {
let Some((t, chr)) = fmt.trim() else {
return Ok(());
};
if !t.left() {
return Ok(());
}
if let Some(c) = chr {
self.skip_while(|a| a == c)
} else {
self.skip_while(|a| a.is_ascii_whitespace())
}
}
pub fn trim_right(&mut self, fmt: &ReadFmt) -> Result<()> {
let Some((t, chr)) = fmt.trim() else {
return Ok(());
};
if !t.right() {
return Ok(());
}
if let Some(c) = chr {
self.skip_while(|a| a == c)
} else {
self.skip_while(|a| a.is_ascii_whitespace())
}
}
pub fn unnext(&mut self, c: char) {
self.pos = self.pos.saturating_sub(c.len_utf8());
self.undone.push(c);
}
pub fn prepend<I: IntoIterator<Item = char>>(&mut self, s: I)
where
I::IntoIter: DoubleEndedIterator,
{
for c in s.into_iter().rev() {
self.unnext(c);
}
}
fn next_inner(&mut self) -> Result<Option<char>> {
let r = match &mut self.source {
ReaderSource::Io(io) => read_char(io.as_mut()),
ReaderSource::Str(s) => Ok(s[self.pos..].chars().next()),
ReaderSource::Iter(i) => Ok(i.next()),
ReaderSource::IterErr(i) => i.next().transpose(),
};
self.res(r)
}
fn res<T>(&self, res: Result<T>) -> Result<T> {
res.map_err(|e| self.map_err(e))
}
fn new(source: ReaderSource<'a>) -> Self {
Self {
source,
pos: 0,
undone: vec![],
}
}
}
fn read_char<R: Read + ?Sized>(r: &mut R) -> Result<Option<char>> {
let mut bts = [0; 4];
if r.read(&mut bts[..1])? != 1 {
return Ok(None);
}
let (len, mut res) = utf8_len(bts[0])?;
if len == 1 {
return Ok(Some(res as u8 as char));
}
if r.read(&mut bts[1..len])? != len - 1 {
return Err(ArgError::failed_to_parse(
"Utf8 expected more bytes.",
String::new(),
));
}
if bts[0] == 0xC0
|| bts[0] == 0xC1
|| (bts[0] == 0xE0 && bts[1] < 0xA0)
|| (bts[0] == 0xF4 && bts[1] < 0x90)
{
return Err(ArgError::failed_to_parse(
"Utf8 overlong encoding.",
String::new(),
));
}
for b in &bts[1..len] {
if (b & 0xC0) != 0x80 {
return Err(ArgError::failed_to_parse(
"Invalid utf8 trailing byte.",
String::new(),
));
}
res = (res << 6) | (b & 0x3F) as u32;
}
char::from_u32(res)
.ok_or_else(|| {
ArgError::failed_to_parse("Invalid utf8 code.", String::new())
})
.map(Some)
}
fn utf8_len(b: u8) -> Result<(usize, u32)> {
match b.leading_ones() {
0 => Ok((1, b as u32)),
2 => Ok((2, (b & 0x1F) as u32)),
3 => Ok((3, (b & 0x0F) as u32)),
4 => Ok((4, (b & 0x07) as u32)),
_ => Err(ArgError::failed_to_parse(
"Invalid leading utf8 byte.",
String::new(),
)),
}
}
impl<'a> From<Box<dyn Read + 'a>> for Reader<'a> {
fn from(value: Box<dyn Read + 'a>) -> Self {
Self::new(ReaderSource::Io(value))
}
}
impl<'a> From<Cow<'a, str>> for Reader<'a> {
fn from(value: Cow<'a, str>) -> Self {
Self::new(ReaderSource::Str(value))
}
}
impl<'a> From<&'a str> for Reader<'a> {
fn from(value: &'a str) -> Self {
Cow::Borrowed(value).into()
}
}
impl From<String> for Reader<'_> {
fn from(value: String) -> Self {
Cow::<str>::Owned(value).into()
}
}
impl<'a> From<Box<dyn Iterator<Item = char> + 'a>> for Reader<'a> {
fn from(value: Box<dyn Iterator<Item = char> + 'a>) -> Self {
Self::new(ReaderSource::Iter(value))
}
}
impl<'a> From<Box<dyn Iterator<Item = Result<char>> + 'a>> for Reader<'a> {
fn from(value: Box<dyn Iterator<Item = Result<char>> + 'a>) -> Self {
Self::new(ReaderSource::IterErr(value))
}
}