use crate::escape;
use crate::{Read, Write};
use core::{convert::AsRef, fmt};
pub struct Display<Str>(Str);
impl<Str> Display<Str> {
pub fn new(s: Str) -> Self {
Self(s)
}
}
impl<Str: AsRef<str>> fmt::Display for Display<Str> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
'"'.fmt(f)?;
for c in self.0.as_ref().chars() {
match c {
'\\' | '"' | '\n' | '\r' | '\t' => c.escape_default().try_for_each(|c| c.fmt(f)),
c if (c as u32) < 20 => write!(f, "\\u{:04x}", c as u16),
c => c.fmt(f),
}?
}
'"'.fmt(f)
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
Control,
Escape(escape::Error),
Eof,
Utf8(core::str::Utf8Error),
}
impl Error {
pub fn is_unicode_error(&self) -> bool {
use escape::Error::*;
matches!(
self,
Self::Utf8(_) | Self::Escape(InvalidChar(_) | ExpectedLowSurrogate)
)
}
}
impl_from!(escape::Error, Error, Error::Escape);
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
use Error::*;
match self {
Control => "invalid string control character".fmt(f),
Escape(e) => e.fmt(f),
Eof => "unterminated string".fmt(f),
Utf8(e) => e.fmt(f),
}
}
}
#[derive(Default)]
struct State {
escape: Option<Option<u8>>,
error: Option<Error>,
}
impl State {
fn process(&mut self, c: u8) -> bool {
if let Some(unicode) = &mut self.escape {
if let Some(hex_pos) = unicode {
if escape::decode_hex(c).is_none() {
self.error = Some(escape::Error::InvalidHex(c).into())
} else if *hex_pos < 3 {
*hex_pos += 1
} else {
self.escape = None
}
} else {
if c == b'u' {
*unicode = Some(0)
} else if escape::Lit::try_from(c).is_some() {
self.escape = None
} else {
self.error = Some(escape::Error::InvalidKind(c).into())
}
}
} else {
match c {
b'"' => return true,
b'\\' => self.escape = Some(None),
0..=0x1F => self.error = Some(Error::Control),
_ => return false,
};
}
self.error.is_some()
}
fn finish(self, mut next: impl FnMut() -> Option<u8>) -> Result<(), Error> {
match self.error {
Some(e) => Err(e),
None if self.escape.is_some() => Err(escape::Error::Eof)?,
None if next() != Some(b'"') => Err(Error::Eof),
None => Ok(()),
}
}
}
pub trait Lex: escape::Lex {
fn str_ignore(&mut self) -> Result<(), Error> {
self.str_foreach(|_| ())
}
fn str_foreach(&mut self, f: impl FnMut(u8)) -> Result<(), Error> {
let mut state = State::default();
self.foreach_until(f, |c| state.process(c));
state.finish(|| self.take_next())
}
}
impl<T> Lex for T where T: escape::Lex {}
pub trait LexWrite: escape::Lex + Read + Write {
fn str_bytes(&mut self, bytes: &mut Self::Bytes) -> Result<(), Error> {
let mut state = State::default();
self.write_until(bytes, |c| state.process(c));
state.finish(|| self.take_next())
}
fn str_fold<E: From<Error>, T>(
&mut self,
mut out: T,
on_string: impl Fn(&mut Self::Bytes, &mut T) -> Result<(), E>,
on_escape: impl Fn(&mut Self, &mut T) -> Result<(), E>,
) -> Result<T, E> {
fn string_end(c: u8) -> bool {
matches!(c, b'\\' | b'"' | 0..=0x1F)
}
let mut bytes = Self::Bytes::default();
self.write_until(&mut bytes, string_end);
on_string(&mut bytes, &mut out)?;
match self.take_next().ok_or(Error::Eof)? {
b'\\' => (),
b'"' => return Ok(out),
0..=0x1F => return Err(Error::Control)?,
_ => unreachable!(),
}
loop {
on_escape(self, &mut out)?;
self.write_until(&mut bytes, string_end);
on_string(&mut bytes, &mut out)?;
match self.take_next().ok_or(Error::Eof)? {
b'\\' => continue,
b'"' => return Ok(out),
0..=0x1F => return Err(Error::Control)?,
_ => unreachable!(),
}
}
}
}
impl<T> LexWrite for T where T: Read + Write {}
pub trait LexAlloc: LexWrite {
type Str: AsRef<str>;
fn str_string(&mut self) -> Result<Self::Str, Error>;
}
#[cfg(feature = "alloc")]
impl<'a> LexAlloc for crate::SliceLexer<'a> {
type Str = alloc::borrow::Cow<'a, str>;
fn str_string(&mut self) -> Result<Self::Str, Error> {
use alloc::borrow::Cow;
let on_string = |bytes: &mut Self::Bytes, out: &mut Self::Str| {
match core::str::from_utf8(bytes).map_err(Error::Utf8)? {
"" => (),
s if out.is_empty() => *out = Cow::Borrowed(s),
s => out.to_mut().push_str(s),
};
Ok::<_, Error>(())
};
use crate::escape::Lex;
self.str_fold(Cow::Borrowed(""), on_string, |lexer, out| {
let next = lexer.take_next().ok_or(escape::Error::Eof)?;
out.to_mut()
.push(lexer.escape(next).map_err(Error::Escape)?);
Ok(())
})
}
}
#[cfg(feature = "alloc")]
impl<E, I: Iterator<Item = Result<u8, E>>> LexAlloc for crate::IterLexer<E, I> {
type Str = alloc::string::String;
fn str_string(&mut self) -> Result<Self::Str, Error> {
use alloc::string::String;
let on_string = |bytes: &mut Self::Bytes, out: &mut Self::Str| {
match bytes {
b if b.is_empty() => (),
b if out.is_empty() => {
*out = String::from_utf8(core::mem::take(b))
.map_err(|e| Error::Utf8(e.utf8_error()))?
}
b => out.push_str(core::str::from_utf8(b).map_err(Error::Utf8)?),
}
Ok::<_, Error>(())
};
use crate::escape::Lex;
self.str_fold(Self::Str::new(), on_string, |lexer, out| {
let next = lexer.take_next().ok_or(escape::Error::Eof)?;
out.push(lexer.escape(next).map_err(Error::Escape)?);
Ok(())
})
}
}