miette/
error.rs

1use std::{
2    error::Error,
3    fmt::{self, Display},
4    io,
5};
6
7use crate::Diagnostic;
8
9/**
10Error enum for miette. Used by certain operations in the protocol.
11*/
12#[derive(Debug)]
13pub enum MietteError {
14    /// Wrapper around [`std::io::Error`]. This is returned when something went
15    /// wrong while reading a [`SourceCode`](crate::SourceCode).
16    IoError(io::Error),
17
18    /// Returned when a [`SourceSpan`](crate::SourceSpan) extends beyond the
19    /// bounds of a given [`SourceCode`](crate::SourceCode).
20    OutOfBounds,
21}
22
23impl Display for MietteError {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        match self {
26            MietteError::IoError(error) => write!(f, "{error}"),
27            MietteError::OutOfBounds => {
28                write!(f, "The given offset is outside the bounds of its Source")
29            }
30        }
31    }
32}
33
34impl Error for MietteError {
35    fn source(&self) -> Option<&(dyn Error + 'static)> {
36        match self {
37            MietteError::IoError(error) => error.source(),
38            MietteError::OutOfBounds => None,
39        }
40    }
41}
42
43impl From<io::Error> for MietteError {
44    fn from(value: io::Error) -> Self {
45        Self::IoError(value)
46    }
47}
48
49impl Diagnostic for MietteError {
50    fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
51        match self {
52            MietteError::IoError(_) => Some(Box::new("miette::io_error")),
53            MietteError::OutOfBounds => Some(Box::new("miette::span_out_of_bounds")),
54        }
55    }
56
57    fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
58        match self {
59            MietteError::IoError(_) => None,
60            MietteError::OutOfBounds => Some(Box::new(
61                "Double-check your spans. Do you have an off-by-one error?",
62            )),
63        }
64    }
65
66    fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
67        let crate_version = env!("CARGO_PKG_VERSION");
68        let variant = match self {
69            MietteError::IoError(_) => "#variant.IoError",
70            MietteError::OutOfBounds => "#variant.OutOfBounds",
71        };
72        Some(Box::new(format!(
73            "https://docs.rs/miette/{}/miette/enum.MietteError.html{}",
74            crate_version, variant,
75        )))
76    }
77}
78
79#[cfg(test)]
80pub(crate) mod tests {
81    use std::{error::Error, io::ErrorKind};
82
83    use super::*;
84
85    #[derive(Debug)]
86    pub(crate) struct TestError(pub(crate) io::Error);
87
88    impl Display for TestError {
89        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90            write!(f, "testing, testing...")
91        }
92    }
93
94    impl Error for TestError {
95        fn source(&self) -> Option<&(dyn Error + 'static)> {
96            Some(&self.0)
97        }
98    }
99
100    #[test]
101    fn io_error() {
102        let inner_error = io::Error::new(ErrorKind::Other, "halt and catch fire");
103        let outer_error = TestError(inner_error);
104        let io_error = io::Error::new(ErrorKind::Other, outer_error);
105
106        let miette_error = MietteError::from(io_error);
107
108        assert_eq!(miette_error.to_string(), "testing, testing...");
109        assert_eq!(
110            miette_error.source().unwrap().to_string(),
111            "halt and catch fire"
112        );
113    }
114
115    #[test]
116    fn out_of_bounds() {
117        let miette_error = MietteError::OutOfBounds;
118
119        assert_eq!(
120            miette_error.to_string(),
121            "The given offset is outside the bounds of its Source"
122        );
123        assert_eq!(miette_error.source().map(ToString::to_string), None);
124    }
125}