use super::octets::ParseError;
use core::fmt;
pub struct String<Octets>(Octets);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Symbol {
Char(char),
SimpleEscape(char),
DecimalEscape(u8),
}
impl Symbol {
pub fn from_chars<C>(chars: C) -> Result<Option<Self>, SymbolError>
where
C: IntoIterator<Item = char>,
{
let mut chars = chars.into_iter();
let ch = match chars.next() {
Some(ch) => ch,
None => return Ok(None),
};
if ch != '\\' {
return Ok(Some(Symbol::Char(ch)));
}
match chars.next() {
Some(ch) if ch.is_ascii_digit() => {
let ch = ch.to_digit(10).unwrap() * 100;
let ch2 = match chars.next() {
Some(ch) => match ch.to_digit(10) {
Some(ch) => ch * 10,
None => return Err(SymbolError::BadEscape),
},
None => return Err(SymbolError::ShortInput),
};
let ch3 = match chars.next() {
Some(ch) => match ch.to_digit(10) {
Some(ch) => ch,
None => return Err(SymbolError::BadEscape),
},
None => return Err(SymbolError::ShortInput),
};
let res = ch + ch2 + ch3;
if res > 255 {
return Err(SymbolError::BadEscape);
}
Ok(Some(Symbol::DecimalEscape(res as u8)))
}
Some(ch) => Ok(Some(Symbol::SimpleEscape(ch))),
None => Err(SymbolError::ShortInput),
}
}
pub fn from_octet(ch: u8) -> Self {
if ch == b' ' || ch == b'"' || ch == b'\\' || ch == b';' {
Symbol::SimpleEscape(ch as char)
} else if !(0x20..0x7F).contains(&ch) {
Symbol::DecimalEscape(ch)
} else {
Symbol::Char(ch as char)
}
}
pub fn into_octet(self) -> Result<u8, BadSymbol> {
match self {
Symbol::Char(ch) | Symbol::SimpleEscape(ch) => {
if ch.is_ascii() && ('\u{20}'..='\u{7E}').contains(&ch) {
Ok(ch as u8)
} else {
Err(BadSymbol(self))
}
}
Symbol::DecimalEscape(ch) => Ok(ch),
}
}
pub fn into_char(self) -> Result<char, BadSymbol> {
match self {
Symbol::Char(ch) | Symbol::SimpleEscape(ch) => Ok(ch),
Symbol::DecimalEscape(_) => Err(BadSymbol(self)),
}
}
pub fn into_digit(self, base: u32) -> Result<u32, BadSymbol> {
if let Symbol::Char(ch) = self {
match ch.to_digit(base) {
Some(ch) => Ok(ch),
None => Err(BadSymbol(self)),
}
} else {
Err(BadSymbol(self))
}
}
pub fn is_word_char(self) -> bool {
match self {
Symbol::Char(ch) => {
ch != ' '
&& ch != '\t'
&& ch != '('
&& ch != ')'
&& ch != ';'
&& ch != '"'
}
_ => true,
}
}
}
impl From<char> for Symbol {
fn from(ch: char) -> Symbol {
Symbol::Char(ch)
}
}
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Symbol::Char(ch) => write!(f, "{}", ch),
Symbol::SimpleEscape(ch) => write!(f, "\\{}", ch),
Symbol::DecimalEscape(ch) => write!(f, "\\{:03}", ch),
}
}
}
#[derive(Clone, Debug)]
pub struct Symbols<Chars> {
chars: Option<Chars>,
}
impl<Chars> Symbols<Chars> {
pub fn new(chars: Chars) -> Self {
Symbols { chars: Some(chars) }
}
}
impl<Chars: Iterator<Item = char>> Iterator for Symbols<Chars> {
type Item = Symbol;
fn next(&mut self) -> Option<Self::Item> {
if let Ok(res) = Symbol::from_chars(self.chars.as_mut()?) {
return res;
}
self.chars = None;
None
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SymbolError {
BadEscape,
ShortInput,
}
impl fmt::Display for SymbolError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SymbolError::BadEscape => f.write_str("illegal escape sequence"),
SymbolError::ShortInput => ParseError::ShortInput.fmt(f),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for SymbolError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BadSymbol(pub Symbol);
impl fmt::Display for BadSymbol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "unexpected symbol '{}'", self.0)
}
}
#[cfg(feature = "std")]
impl std::error::Error for BadSymbol {}
#[cfg(feature = "std")]
impl From<BadSymbol> for std::io::Error {
fn from(err: BadSymbol) -> Self {
std::io::Error::new(std::io::ErrorKind::Other, err)
}
}