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