extern crate alloc; #[cfg(not(feature = "std"))] use crate::StdError as Error; use alloc::boxed::Box; use core::fmt::{self, Display}; #[cfg(feature = "std")] use std::error::Error; #[cfg(feature = "std")] use std::io; use crate::Diagnostic; /** Error enum for miette. Used by certain operations in the protocol. */ #[derive(Debug)] pub enum MietteError { /// Wrapper around [`io::Error`]. This is returned when something went /// wrong while reading a [`SourceCode`](crate::SourceCode). #[cfg(feature = "std")] IoError(io::Error), /// Returned when a [`SourceSpan`](crate::SourceSpan) extends beyond the /// bounds of a given [`SourceCode`](crate::SourceCode). OutOfBounds, } impl Display for MietteError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { #[cfg(feature = "std")] MietteError::IoError(error) => write!(f, "{error}"), MietteError::OutOfBounds => { write!(f, "The given offset is outside the bounds of its Source") } } } } impl Error for MietteError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { #[cfg(feature = "std")] MietteError::IoError(error) => error.source(), MietteError::OutOfBounds => None, } } } #[cfg(feature = "std")] impl From for MietteError { fn from(value: io::Error) -> Self { Self::IoError(value) } } impl Diagnostic for MietteError { fn code<'a>(&'a self) -> Option> { match self { #[cfg(feature = "std")] MietteError::IoError(_) => Some(Box::new("miette::io_error")), MietteError::OutOfBounds => Some(Box::new("miette::span_out_of_bounds")), } } fn help<'a>(&'a self) -> Option> { match self { #[cfg(feature = "std")] MietteError::IoError(_) => None, MietteError::OutOfBounds => Some(Box::new( "Double-check your spans. Do you have an off-by-one error?", )), } } fn url<'a>(&'a self) -> Option> { let crate_version = env!("CARGO_PKG_VERSION"); let variant = match self { #[cfg(feature = "std")] MietteError::IoError(_) => "#variant.IoError", MietteError::OutOfBounds => "#variant.OutOfBounds", }; Some(Box::new(alloc::format!( "https://docs.rs/miette/{}/miette/enum.MietteError.html{}", crate_version, variant, ))) } } #[cfg(test)] pub(crate) mod tests { #[cfg(not(feature = "std"))] use crate::StdError as Error; use std::string::ToString; use super::*; #[derive(Debug)] #[cfg(feature = "std")] pub(crate) struct TestError(pub(crate) io::Error); #[cfg(not(feature = "std"))] pub(crate) struct TestError(pub(crate) &'static str); impl Display for TestError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "testing, testing...") } } #[cfg(feature = "std")] impl Error for TestError { fn source(&self) -> Option<&(dyn Error + 'static)> { Some(&self.0) } } #[cfg(not(feature = "std"))] impl Error for TestError { fn source(&self) -> Option<&(dyn Error + 'static)> { None } } #[cfg(feature = "std")] #[test] fn io_error() { let inner_error = io::Error::other("halt and catch fire"); let outer_error = TestError(inner_error); let io_error = io::Error::other(outer_error); let miette_error = MietteError::from(io_error); assert_eq!(miette_error.to_string(), "testing, testing..."); assert_eq!( miette_error.source().unwrap().to_string(), "halt and catch fire" ); } #[test] fn out_of_bounds() { let miette_error = MietteError::OutOfBounds; assert_eq!( miette_error.to_string(), "The given offset is outside the bounds of its Source" ); assert_eq!(miette_error.source().map(ToString::to_string), None); } }