1use std::{
2 error::Error,
3 fmt::{self, Display},
4 io,
5};
6
7use crate::Diagnostic;
8
9#[derive(Debug)]
13pub enum MietteError {
14 IoError(io::Error),
17
18 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}