use std::error;
use std::fmt::{self, Debug, Display};
use std::io;
use std::result;
pub struct Error {
err: Box<ErrorImpl>,
}
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Location {
line: usize,
column: usize,
}
impl Location {
pub fn line(&self) -> usize {
self.line
}
pub fn column(&self) -> usize {
self.column
}
}
impl Error {
pub fn location(&self) -> Option<Location> {
self.err.location
}
pub fn classify(&self) -> Category {
match self.err.code {
ErrorCode::Io(_) => Category::Io,
ErrorCode::EofWhileParsingList
| ErrorCode::EofWhileParsingString
| ErrorCode::EofWhileParsingVector
| ErrorCode::EofWhileParsingValue
| ErrorCode::EofWhileParsingCharacterConstant => Category::Eof,
ErrorCode::ExpectedSomeIdent
| ErrorCode::ExpectedSomeValue
| ErrorCode::ExpectedVector
| ErrorCode::ExpectedOctet
| ErrorCode::MismatchedParenthesis
| ErrorCode::InvalidEscape
| ErrorCode::InvalidNumber
| ErrorCode::InvalidCharacterConstant
| ErrorCode::InvalidSymbol
| ErrorCode::NumberOutOfRange
| ErrorCode::InvalidUnicodeCodePoint
| ErrorCode::TrailingCharacters
| ErrorCode::RecursionLimitExceeded => Category::Syntax,
}
}
pub fn is_io(&self) -> bool {
self.classify() == Category::Io
}
pub fn is_syntax(&self) -> bool {
self.classify() == Category::Syntax
}
pub fn is_eof(&self) -> bool {
self.classify() == Category::Eof
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Category {
Io,
Syntax,
Eof,
}
impl From<Error> for io::Error {
fn from(l: Error) -> Self {
if let ErrorCode::Io(err) = l.err.code {
err
} else {
match l.classify() {
Category::Io => unreachable!(),
Category::Syntax => io::Error::new(io::ErrorKind::InvalidData, l),
Category::Eof => io::Error::new(io::ErrorKind::UnexpectedEof, l),
}
}
}
}
impl Error {
pub(crate) fn syntax(code: ErrorCode, line: usize, column: usize) -> Self {
Error {
err: Box::new(ErrorImpl {
code,
location: Some(Location { line, column }),
}),
}
}
pub(crate) fn io(error: io::Error) -> Self {
Error {
err: Box::new(ErrorImpl {
code: ErrorCode::Io(error),
location: None,
}),
}
}
}
struct ErrorImpl {
code: ErrorCode,
location: Option<Location>,
}
pub(crate) enum ErrorCode {
Io(io::Error),
EofWhileParsingList,
EofWhileParsingVector,
EofWhileParsingString,
EofWhileParsingValue,
EofWhileParsingCharacterConstant,
ExpectedSomeIdent,
MismatchedParenthesis,
ExpectedSomeValue,
ExpectedVector,
ExpectedOctet,
InvalidEscape,
InvalidNumber,
InvalidSymbol,
NumberOutOfRange,
InvalidUnicodeCodePoint,
InvalidCharacterConstant,
TrailingCharacters,
RecursionLimitExceeded,
}
impl Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ErrorCode::Io(ref err) => Display::fmt(err, f),
ErrorCode::EofWhileParsingList => f.write_str("EOF while parsing a list"),
ErrorCode::EofWhileParsingVector => f.write_str("EOF while parsing a vector"),
ErrorCode::EofWhileParsingString => f.write_str("EOF while parsing a string"),
ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
ErrorCode::EofWhileParsingCharacterConstant => {
f.write_str("EOF while parsing a character constant")
}
ErrorCode::ExpectedSomeIdent => f.write_str("expected ident"),
ErrorCode::ExpectedSomeValue => f.write_str("expected value"),
ErrorCode::ExpectedVector => f.write_str("expected vector"),
ErrorCode::ExpectedOctet => f.write_str("expected octet"),
ErrorCode::InvalidEscape => f.write_str("invalid escape"),
ErrorCode::InvalidNumber => f.write_str("invalid number"),
ErrorCode::InvalidSymbol => f.write_str("invalid symbol"),
ErrorCode::MismatchedParenthesis => f.write_str("mismatched parenthesis"),
ErrorCode::NumberOutOfRange => f.write_str("number out of range"),
ErrorCode::InvalidUnicodeCodePoint => f.write_str("invalid unicode code point"),
ErrorCode::InvalidCharacterConstant => f.write_str("invalid character constant"),
ErrorCode::TrailingCharacters => f.write_str("trailing characters"),
ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self.err.code {
ErrorCode::Io(ref err) => Some(err),
_ => None,
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&*self.err, f)
}
}
impl Display for ErrorImpl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(loc) = self.location {
write!(
f,
"{} at line {} column {}",
self.code, loc.line, loc.column
)
} else {
Display::fmt(&self.code, f)
}
}
}
impl Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(loc) = self.err.location {
write!(
f,
"Error({:?}, line: {}, column: {})",
self.err.code.to_string(),
loc.line,
loc.column,
)
} else {
write!(f, "Error({:?})", self.err.code.to_string())
}
}
}