headless_lms_utils/error/
util_error.rs

1/*!
2Contains error and result types for all the util functions.
3*/
4
5use std::fmt::Display;
6
7use backtrace::Backtrace;
8use tracing_error::SpanTrace;
9
10use super::backend_error::BackendError;
11
12/**
13Used as the result types for all utils.
14
15See also [UtilError] for documentation on how to return errors from models.
16*/
17pub type UtilResult<T> = Result<T, UtilError>;
18
19/// The type of [UtilError] that occured.
20#[derive(Debug)]
21pub enum UtilErrorType {
22    UrlParse,
23    Walkdir,
24    StripPrefix,
25    TokioIo,
26    SerdeJson,
27    CloudStorage,
28    Other,
29    Unavailable,
30    DeserializationError,
31    TmcHttpError,
32    TmcErrorResponse,
33}
34
35/**
36Error type used by all models. Used as the error type in [UtilError], which is used by all the controllers in the application.
37
38All the information in the error is meant to be seen by the user. The type of error is determined by the [UtilErrorType] enum, which is stored inside this struct.
39
40## Examples
41
42### Usage without source error
43
44```no_run
45# use headless_lms_utils::prelude::*;
46# fn random_function() -> UtilResult<()> {
47#    let erroneous_condition = 1 == 1;
48if erroneous_condition {
49    return Err(UtilError::new(
50        UtilErrorType::Other,
51        "File not found".to_string(),
52        None,
53    ));
54}
55# Ok(())
56# }
57```
58
59### Usage with a source error
60
61Used when calling a function that returns an error that cannot be automatically converted to an UtilError. (See `impl From<X>` implementations on this struct.)
62
63```no_run
64# use headless_lms_utils::prelude::*;
65# fn some_function_returning_an_error() -> UtilResult<()> {
66#    return Err(UtilError::new(
67#        UtilErrorType::Other,
68#        "File not found".to_string(),
69#        None,
70#    ));
71# }
72#
73# fn random_function() -> UtilResult<()> {
74#    let erroneous_condition = 1 == 1;
75some_function_returning_an_error().map_err(|original_error| {
76    UtilError::new(
77        UtilErrorType::Other,
78        "Library x failed to do y".to_string(),
79        Some(original_error.into()),
80    )
81})?;
82# Ok(())
83# }
84```
85*/
86#[derive(Debug)]
87pub struct UtilError {
88    error_type: <UtilError as BackendError>::ErrorType,
89    message: String,
90    /// Original error that caused this error.
91    source: Option<anyhow::Error>,
92    /// A trace of tokio tracing spans, generated automatically when the error is generated.
93    span_trace: Box<SpanTrace>,
94    /// Stack trace, generated automatically when the error is created.
95    backtrace: Box<Backtrace>,
96}
97
98impl std::error::Error for UtilError {
99    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
100        self.source.as_ref().and_then(|o| o.source())
101    }
102
103    fn cause(&self) -> Option<&dyn std::error::Error> {
104        self.source()
105    }
106}
107
108impl Display for UtilError {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110        write!(f, "UtilError {:?} {:?}", self.error_type, self.message)
111    }
112}
113
114impl BackendError for UtilError {
115    type ErrorType = UtilErrorType;
116
117    fn new<M: Into<String>, S: Into<Option<anyhow::Error>>>(
118        error_type: Self::ErrorType,
119        message: M,
120        source_error: S,
121    ) -> Self {
122        Self::new_with_traces(
123            error_type,
124            message,
125            source_error,
126            Backtrace::new(),
127            SpanTrace::capture(),
128        )
129    }
130
131    fn backtrace(&self) -> Option<&Backtrace> {
132        Some(&self.backtrace)
133    }
134
135    fn error_type(&self) -> &Self::ErrorType {
136        &self.error_type
137    }
138
139    fn message(&self) -> &str {
140        &self.message
141    }
142
143    fn span_trace(&self) -> &SpanTrace {
144        &self.span_trace
145    }
146
147    fn new_with_traces<M: Into<String>, S: Into<Option<anyhow::Error>>>(
148        error_type: Self::ErrorType,
149        message: M,
150        source_error: S,
151        backtrace: Backtrace,
152        span_trace: SpanTrace,
153    ) -> Self {
154        Self {
155            error_type,
156            message: message.into(),
157            source: source_error.into(),
158            span_trace: Box::new(span_trace),
159            backtrace: Box::new(backtrace),
160        }
161    }
162}
163
164impl From<url::ParseError> for UtilError {
165    fn from(source: url::ParseError) -> Self {
166        UtilError::new(
167            UtilErrorType::UrlParse,
168            source.to_string(),
169            Some(source.into()),
170        )
171    }
172}
173
174impl From<walkdir::Error> for UtilError {
175    fn from(source: walkdir::Error) -> Self {
176        UtilError::new(
177            UtilErrorType::Walkdir,
178            source.to_string(),
179            Some(source.into()),
180        )
181    }
182}
183
184impl From<std::path::StripPrefixError> for UtilError {
185    fn from(source: std::path::StripPrefixError) -> Self {
186        UtilError::new(
187            UtilErrorType::StripPrefix,
188            source.to_string(),
189            Some(source.into()),
190        )
191    }
192}
193
194impl From<tokio::io::Error> for UtilError {
195    fn from(source: tokio::io::Error) -> Self {
196        UtilError::new(
197            UtilErrorType::TokioIo,
198            source.to_string(),
199            Some(source.into()),
200        )
201    }
202}
203
204impl From<serde_json::Error> for UtilError {
205    fn from(source: serde_json::Error) -> Self {
206        UtilError::new(
207            UtilErrorType::SerdeJson,
208            source.to_string(),
209            Some(source.into()),
210        )
211    }
212}
213
214impl From<cloud_storage::Error> for UtilError {
215    fn from(source: cloud_storage::Error) -> Self {
216        UtilError::new(
217            UtilErrorType::CloudStorage,
218            source.to_string(),
219            Some(source.into()),
220        )
221    }
222}
223
224impl From<anyhow::Error> for UtilError {
225    fn from(err: anyhow::Error) -> UtilError {
226        Self::new(UtilErrorType::Other, err.to_string(), Some(err))
227    }
228}