use std::{fmt, io};
use thiserror::Error;
#[cfg(feature = "backtrace")]
use crate::trace;
use crate::{
error::{ProtoError, ProtoErrorKind},
rr::RecordType,
serialize::txt::Token,
};
#[cfg(feature = "backtrace")]
#[cfg_attr(docsrs, doc(cfg(feature = "backtrace")))]
use backtrace::Backtrace as ExtBacktrace;
pub type ParseResult<T> = ::std::result::Result<T, ParseError>;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ParseErrorKind {
#[error("invalid numerical character: {0}")]
CharToInt(char),
#[error("{0}")]
Message(&'static str),
#[error("token is missing: {0}")]
MissingToken(String),
#[error("{0}")]
Msg(String),
#[error("invalid time string: {0}")]
ParseTime(String),
#[error("unrecognized token in stream: {0:?}")]
UnexpectedToken(Token),
#[error("network address parse error: {0}")]
AddrParse(#[from] std::net::AddrParseError),
#[error("data encoding error: {0}")]
DataEncoding(#[from] data_encoding::DecodeError),
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("lexer error: {0}")]
Lexer(#[from] LexerError),
#[error("error parsing number: {0}")]
ParseInt(#[from] std::num::ParseIntError),
#[error("proto error: {0}")]
Proto(#[from] ProtoError),
#[error("unknown RecordType: {0}")]
UnknownRecordType(u16),
#[error("unsupported RecordType: {0}")]
UnsupportedRecordType(RecordType),
#[error("request timed out")]
Timeout,
}
impl Clone for ParseErrorKind {
fn clone(&self) -> Self {
use ParseErrorKind::*;
match self {
CharToInt(c) => CharToInt(*c),
Message(msg) => Message(msg),
MissingToken(ref s) => MissingToken(s.clone()),
Msg(ref msg) => Msg(msg.clone()),
ParseTime(ref s) => ParseTime(s.clone()),
UnexpectedToken(ref token) => UnexpectedToken(token.clone()),
AddrParse(e) => AddrParse(e.clone()),
DataEncoding(e) => DataEncoding(*e),
Io(e) => Io(std::io::Error::from(e.kind())),
Lexer(e) => Lexer(e.clone()),
ParseInt(e) => ParseInt(e.clone()),
Proto(e) => Proto(e.clone()),
UnsupportedRecordType(ty) => UnsupportedRecordType(*ty),
UnknownRecordType(ty) => UnknownRecordType(*ty),
Timeout => Timeout,
}
}
}
#[derive(Error, Debug)]
pub struct ParseError {
kind: ParseErrorKind,
#[cfg(feature = "backtrace")]
backtrack: Option<ExtBacktrace>,
}
impl ParseError {
pub fn kind(&self) -> &ParseErrorKind {
&self.kind
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
cfg_if::cfg_if! {
if #[cfg(feature = "backtrace")] {
if let Some(ref backtrace) = self.backtrack {
fmt::Display::fmt(&self.kind, f)?;
fmt::Debug::fmt(backtrace, f)
} else {
fmt::Display::fmt(&self.kind, f)
}
} else {
fmt::Display::fmt(&self.kind, f)
}
}
}
}
impl From<ParseErrorKind> for ParseError {
fn from(kind: ParseErrorKind) -> Self {
Self {
kind,
#[cfg(feature = "backtrace")]
backtrack: trace!(),
}
}
}
impl From<&'static str> for ParseError {
fn from(msg: &'static str) -> Self {
ParseErrorKind::Message(msg).into()
}
}
impl From<String> for ParseError {
fn from(msg: String) -> Self {
ParseErrorKind::Msg(msg).into()
}
}
impl From<std::net::AddrParseError> for ParseError {
fn from(e: std::net::AddrParseError) -> Self {
ParseErrorKind::from(e).into()
}
}
impl From<::data_encoding::DecodeError> for ParseError {
fn from(e: data_encoding::DecodeError) -> Self {
ParseErrorKind::from(e).into()
}
}
impl From<io::Error> for ParseError {
fn from(e: io::Error) -> Self {
match e.kind() {
io::ErrorKind::TimedOut => ParseErrorKind::Timeout.into(),
_ => ParseErrorKind::from(e).into(),
}
}
}
impl From<LexerError> for ParseError {
fn from(e: LexerError) -> Self {
ParseErrorKind::from(e).into()
}
}
impl From<std::num::ParseIntError> for ParseError {
fn from(e: std::num::ParseIntError) -> Self {
ParseErrorKind::from(e).into()
}
}
impl From<ProtoError> for ParseError {
fn from(e: ProtoError) -> Self {
match *e.kind() {
ProtoErrorKind::Timeout => ParseErrorKind::Timeout.into(),
_ => ParseErrorKind::from(e).into(),
}
}
}
impl From<std::convert::Infallible> for ParseError {
fn from(_e: std::convert::Infallible) -> Self {
panic!("infallible")
}
}
impl From<ParseError> for io::Error {
fn from(e: ParseError) -> Self {
match *e.kind() {
ParseErrorKind::Timeout => Self::new(io::ErrorKind::TimedOut, e),
_ => Self::new(io::ErrorKind::Other, e),
}
}
}
pub(crate) type LexerResult<T> = ::std::result::Result<T, LexerError>;
#[derive(Eq, PartialEq, Debug, Error, Clone)]
#[non_exhaustive]
pub enum LexerErrorKind {
#[error("unexpected end of input")]
EOF,
#[error("illegal character input: {0}")]
IllegalCharacter(char),
#[error("illegal state: {0}")]
IllegalState(&'static str),
#[error("{0}")]
Message(&'static str),
#[error("unclosed list, missing ')'")]
UnclosedList,
#[error("unclosed quoted string")]
UnclosedQuotedString,
#[error("unrecognized character input: {0}")]
UnrecognizedChar(char),
#[error("unrecognized dollar content: {0}")]
UnrecognizedDollar(String),
#[error("unrecognized octet: {0:x}")]
UnrecognizedOctet(u32),
}
#[derive(Clone, Error, Debug)]
pub struct LexerError {
kind: LexerErrorKind,
#[cfg(feature = "backtrace")]
backtrack: Option<ExtBacktrace>,
}
impl LexerError {
pub fn kind(&self) -> &LexerErrorKind {
&self.kind
}
}
impl From<LexerErrorKind> for LexerError {
fn from(kind: LexerErrorKind) -> Self {
Self {
kind,
#[cfg(feature = "backtrace")]
backtrack: trace!(),
}
}
}
impl fmt::Display for LexerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
cfg_if::cfg_if! {
if #[cfg(feature = "backtrace")] {
if let Some(ref backtrace) = self.backtrack {
fmt::Display::fmt(&self.kind, f)?;
fmt::Debug::fmt(backtrace, f)
} else {
fmt::Display::fmt(&self.kind, f)
}
} else {
fmt::Display::fmt(&self.kind, f)
}
}
}
}