miette/src/error.rs

157 lines
4.3 KiB
Rust

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<io::Error> for MietteError {
fn from(value: io::Error) -> Self {
Self::IoError(value)
}
}
impl Diagnostic for MietteError {
fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
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<Box<dyn fmt::Display + 'a>> {
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<Box<dyn fmt::Display + 'a>> {
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;
#[cfg(not(feature = "std"))]
use alloc::string::ToString;
#[cfg(feature = "std")]
use std::string::ToString;
use super::*;
#[derive(Debug)]
#[cfg(feature = "std")]
pub(crate) struct TestError(pub(crate) io::Error);
#[cfg(not(feature = "std"))]
#[derive(Debug)]
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);
}
}