melib/email/parser: add backtrace field to ParsingError

Add backtrace field to ParsingError when the build is for testing or
documentation.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
This commit is contained in:
Manos Pitsidianakis 2023-12-26 00:48:16 +02:00
parent ab1b946fd9
commit f685726eac
No known key found for this signature in database
GPG Key ID: 7729C7707F7E09D0

View File

@ -22,6 +22,8 @@
//! Parsers for email. See submodules. //! Parsers for email. See submodules.
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
#[cfg(any(test, doc))]
use std::backtrace::Backtrace;
use std::{borrow::Cow, convert::TryFrom, fmt::Write}; use std::{borrow::Cow, convert::TryFrom, fmt::Write};
use nom::{ use nom::{
@ -51,29 +53,58 @@ macro_rules! to_str {
unsafe { std::str::from_utf8_unchecked($l) } unsafe { std::str::from_utf8_unchecked($l) }
}}; }};
} }
#[derive(Eq, PartialEq)]
pub struct ParsingError<I> { pub struct ParsingError<I> {
pub input: I, pub input: I,
pub error: Cow<'static, str>, pub error: Cow<'static, str>,
#[cfg(any(test, doc))]
pub backtrace: Backtrace,
}
impl<I: PartialEq> PartialEq for ParsingError<I> {
fn eq(&self, other: &Self) -> bool {
self.input.eq(&other.input) && self.error.eq(&other.error)
}
} }
impl std::fmt::Debug for ParsingError<&'_ [u8]> { impl std::fmt::Debug for ParsingError<&'_ [u8]> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
#[cfg(any(test, doc))]
{
fmt.debug_struct("ParsingError")
.field("input", &to_str!(self.input))
.field("error", &self.error)
.field("backtrace", &self.backtrace)
.finish()
}
#[cfg(not(any(test, doc)))]
{
fmt.debug_struct("ParsingError") fmt.debug_struct("ParsingError")
.field("input", &to_str!(self.input)) .field("input", &to_str!(self.input))
.field("error", &self.error) .field("error", &self.error)
.finish() .finish()
} }
} }
}
impl std::fmt::Debug for ParsingError<&'_ str> { impl std::fmt::Debug for ParsingError<&'_ str> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
#[cfg(any(test, doc))]
{
fmt.debug_struct("ParsingError")
.field("input", &self.input)
.field("error", &self.error)
.field("backtrace", &self.backtrace)
.finish()
}
#[cfg(not(any(test, doc)))]
{
fmt.debug_struct("ParsingError") fmt.debug_struct("ParsingError")
.field("input", &self.input) .field("input", &self.input)
.field("error", &self.error) .field("error", &self.error)
.finish() .finish()
} }
} }
}
struct DebugOkWrapper<'r, I, R: AsRef<[u8]>>(&'r IResult<I, R>); struct DebugOkWrapper<'r, I, R: AsRef<[u8]>>(&'r IResult<I, R>);
@ -94,6 +125,8 @@ impl<'i> ParsingError<&'i str> {
ParsingError { ParsingError {
input: self.input.as_bytes(), input: self.input.as_bytes(),
error: self.error, error: self.error,
#[cfg(any(test, doc))]
backtrace: self.backtrace,
} }
} }
} }
@ -103,6 +136,8 @@ impl<I> From<(I, &'static str)> for ParsingError<I> {
Self { Self {
input, input,
error: error.into(), error: error.into(),
#[cfg(any(test, doc))]
backtrace: Backtrace::capture(),
} }
} }
} }
@ -112,6 +147,8 @@ impl<I> From<(I, String)> for ParsingError<I> {
Self { Self {
input, input,
error: error.into(), error: error.into(),
#[cfg(any(test, doc))]
backtrace: Backtrace::capture(),
} }
} }
} }
@ -121,6 +158,8 @@ impl<I> nom::error::ParseError<I> for ParsingError<I> {
Self { Self {
input, input,
error: kind.description().to_string().into(), error: kind.description().to_string().into(),
#[cfg(any(test, doc))]
backtrace: Backtrace::capture(),
} }
} }
@ -128,6 +167,8 @@ impl<I> nom::error::ParseError<I> for ParsingError<I> {
Self { Self {
input, input,
error: format!("{}, {}", kind.description(), other.error).into(), error: format!("{}, {}", kind.description(), other.error).into(),
#[cfg(any(test, doc))]
backtrace: Backtrace::capture(),
} }
} }
} }
@ -137,6 +178,8 @@ impl<I, E> nom::error::FromExternalError<I, E> for ParsingError<I> {
Self { Self {
input, input,
error: kind.description().to_string().into(), error: kind.description().to_string().into(),
#[cfg(any(test, doc))]
backtrace: Backtrace::capture(),
} }
} }
} }
@ -145,7 +188,8 @@ impl<I> nom::error::ContextError<I> for ParsingError<I> {}
impl<'i> From<ParsingError<&'i [u8]>> for Error { impl<'i> From<ParsingError<&'i [u8]>> for Error {
fn from(val: ParsingError<&'i [u8]>) -> Self { fn from(val: ParsingError<&'i [u8]>) -> Self {
Self::new("Parsing error").set_summary(format!( Self::new("Parsing error")
.set_summary(format!(
r#"In input: "{}...", r#"In input: "{}...",
Error: {}"#, Error: {}"#,
String::from_utf8_lossy(val.input) String::from_utf8_lossy(val.input)
@ -154,17 +198,53 @@ Error: {}"#,
.collect::<String>(), .collect::<String>(),
val.error val.error
)) ))
.set_details({
#[cfg(any(test, doc))]
{
println!(
"\tInput:\n{}\tError:\n{}\n\tBacktrace:\n{}",
String::from_utf8_lossy(val.input)
.chars()
.take(30)
.collect::<String>(),
val.error,
val.backtrace
);
val.backtrace.to_string()
}
#[cfg(not(any(test, doc)))]
{
""
}
})
} }
} }
impl<'i> From<ParsingError<&'i str>> for Error { impl<'i> From<ParsingError<&'i str>> for Error {
fn from(val: ParsingError<&'i str>) -> Self { fn from(val: ParsingError<&'i str>) -> Self {
Self::new("Parsing error").set_summary(format!( Self::new("Parsing error")
.set_summary(format!(
r#"In input: "{}...", r#"In input: "{}...",
Error: {}"#, Error: {}"#,
val.input.chars().take(30).collect::<String>(), val.input.chars().take(30).collect::<String>(),
val.error val.error
)) ))
.set_details({
#[cfg(any(test, doc))]
{
println!(
"\tInput:\n{}\tError:\n{}\n\tBacktrace:\n{}",
val.input.chars().take(30).collect::<String>(),
val.error,
val.backtrace
);
val.backtrace.to_string()
}
#[cfg(not(any(test, doc)))]
{
""
}
})
} }
} }