sdml_errors/
errors.rs

1/*!
2Provides the project-wide Error and Result types as well as helper functions.
3 */
4
5use crate::diagnostics::Diagnostic;
6use std::fmt::{Debug, Display};
7use tracing::error;
8
9// ------------------------------------------------------------------------------------------------
10// Public Types  Error and Result
11// ------------------------------------------------------------------------------------------------
12
13///
14/// The Error type for this crate.
15///
16#[derive(Debug)]
17pub enum Error {
18    /// An error was signaled by the standard library I/O functions.
19    IoError {
20        source: std::io::Error,
21    },
22    /// An error was signaled by the standard string conversion functions.
23    Utf8Error {
24        source: core::str::Utf8Error,
25    },
26    /// An error was signaled by the standard string conversion functions.
27    FromUtf8Error {
28        source: std::string::FromUtf8Error,
29    },
30    /// An error was signaled while parsing a `Url`. Note that the methods `from_file_path` and
31    /// `from_directory_path` return `()` on error.
32    UrlParseError {
33        source: Option<url::ParseError>,
34    },
35    /// An error was signaled while parsing a tracing filter expression.
36    TracingFilterError {
37        source: tracing_subscriber::filter::ParseError,
38    },
39    /// An error was signaled while initializing a tracing subscriber..
40    TracingSubscriberError {
41        source: tracing::subscriber::SetGlobalDefaultError,
42    },
43    CodespanReportingError {
44        source: codespan_reporting::files::Error,
45    },
46    /// This allows for a complete `Diagnostic` structure to be passed as an Error.
47    LanguageValidationError {
48        source: Diagnostic,
49    },
50    LanguageTagError {
51        source: language_tags::ParseError,
52    },
53    #[cfg(feature = "templates")]
54    Template {
55        source: tera::Error,
56    },
57    GeneratorError {
58        name: String,
59        message: String,
60    },
61}
62
63///
64/// A Result type that specifically uses this crate's Error.
65///
66pub type Result<T> = std::result::Result<T, Error>;
67
68// ------------------------------------------------------------------------------------------------
69// Private Macros
70// ------------------------------------------------------------------------------------------------
71
72macro_rules! report_and_return {
73    ($err: expr) => {
74        let err = $err;
75        error!("{}", err);
76        return err;
77    };
78}
79
80// ------------------------------------------------------------------------------------------------
81// Implementations
82// ------------------------------------------------------------------------------------------------
83
84impl Display for Error {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        write!(
87            f,
88            "{}",
89            match self {
90                Self::IoError { source } => format!("An I/O error occurred; source: {source}"),
91                Self::Utf8Error { source } =>
92                    format!("A UTF-8 conversion error occurred; source: {source}"),
93                Self::FromUtf8Error { source } =>
94                    format!("A UTF-8 conversion error occurred; source: {source}"),
95                Self::UrlParseError { source } =>
96                    if let Some(source) = source {
97                        format!("An error occurred parsing a URL; source: {source}")
98                    } else {
99                        "An error occurred creating a URL from a file path".to_string()
100                    },
101                Self::TracingFilterError { source } =>
102                    format!("A error occurred parsing a tracing filter; source: {source}"),
103                Self::TracingSubscriberError { source } =>
104                    format!("A error occurred setting the tracing subscriber; source: {source}"),
105                Self::CodespanReportingError { source } =>
106                    format!("An error occurred formatting codespan reports; source: {source}"),
107                Self::LanguageValidationError { source } =>
108                    format!("Validation diagnostic: {}", source.message),
109                Self::LanguageTagError { source } =>
110                    format!("An error occurred parsing a BCP-47 language tag; source: {source}"),
111                Self::GeneratorError { name, message } =>
112                    format!("An error occurred in a generator named `{name}`: {message}"),
113                #[cfg(feature = "templates")]
114                Self::Template { source } =>
115                    format!("An error occurred in the template generator; source: {source}"),
116            }
117        )
118    }
119}
120
121impl std::error::Error for Error {
122    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
123        #[allow(unreachable_patterns)]
124        match self {
125            Error::IoError { source } => Some(source),
126            Error::Utf8Error { source } => Some(source),
127            Self::FromUtf8Error { source } => Some(source),
128            Self::TracingFilterError { source } => Some(source),
129            Self::TracingSubscriberError { source } => Some(source),
130            Self::CodespanReportingError { source } => Some(source),
131            #[cfg(feature = "templates")]
132            Self::Template { source } => Some(source),
133            _ => None,
134        }
135    }
136}
137
138impl<T> From<Error> for Result<T> {
139    fn from(value: Error) -> Self {
140        Err(value)
141    }
142}
143
144impl From<std::io::Error> for Error {
145    fn from(source: std::io::Error) -> Self {
146        report_and_return! {
147            Error::IoError { source }
148        }
149    }
150}
151
152impl From<core::str::Utf8Error> for Error {
153    fn from(source: core::str::Utf8Error) -> Self {
154        report_and_return! {
155            Error::Utf8Error { source }
156        }
157    }
158}
159
160impl From<std::string::FromUtf8Error> for Error {
161    fn from(source: std::string::FromUtf8Error) -> Self {
162        report_and_return! {
163            Error::FromUtf8Error { source }
164        }
165    }
166}
167
168impl From<url::ParseError> for Error {
169    fn from(source: url::ParseError) -> Self {
170        report_and_return! {
171            Error::UrlParseError { source: Some(source) }
172        }
173    }
174}
175
176impl From<tracing_subscriber::filter::ParseError> for Error {
177    fn from(source: tracing_subscriber::filter::ParseError) -> Self {
178        report_and_return! {
179            Error::TracingFilterError { source }
180        }
181    }
182}
183
184impl From<tracing::subscriber::SetGlobalDefaultError> for Error {
185    fn from(source: tracing::subscriber::SetGlobalDefaultError) -> Self {
186        report_and_return! {
187            Error::TracingSubscriberError { source }
188        }
189    }
190}
191
192impl From<codespan_reporting::files::Error> for Error {
193    fn from(source: codespan_reporting::files::Error) -> Self {
194        report_and_return! {
195            Error::CodespanReportingError { source }
196        }
197    }
198}
199
200impl From<language_tags::ParseError> for Error {
201    fn from(source: language_tags::ParseError) -> Self {
202        report_and_return! {
203            Error::LanguageTagError { source }
204        }
205    }
206}
207
208impl From<Diagnostic> for Error {
209    fn from(source: Diagnostic) -> Self {
210        report_and_return! {
211            Self::LanguageValidationError { source }
212        }
213    }
214}
215
216#[cfg(feature = "templates")]
217impl From<tera::Error> for Error {
218    fn from(source: tera::Error) -> Self {
219        report_and_return! {
220            Self::Template { source }
221        }
222    }
223}