stackerror/
error.rs

1//! Provides the [`StackError`] struct which implements the [`ErrorStacks`]
2//! trait.
3
4use crate::codes::ErrorCode;
5
6/// Trait for stacking errors: errors that stack and provide an optional error
7/// code and resource URI for runtime error handling.
8pub trait ErrorStacks<C>
9where
10    C: Send + Sync + 'static + Eq + PartialEq + Clone,
11{
12    /// Get the error code if one is set.
13    fn err_code(&self) -> Option<&C>;
14    /// Set the error code.
15    fn with_err_code(self, code: C) -> Self;
16    /// Remove the error code.
17    fn with_no_err_code(self) -> Self;
18    /// Get the error URI if one is set.
19    fn err_uri(&self) -> Option<&str>;
20    /// Set the error URI.
21    fn with_err_uri(self, uri: String) -> Self;
22    /// Remove the error URI.
23    fn with_no_err_uri(self) -> Self;
24    /// Set the error message.
25    fn with_err_msg(self, error: impl std::fmt::Display + Send + Sync + 'static) -> Self;
26    /// Remove the error message.
27    fn with_no_err_msg(self) -> Self;
28    /// Stack a new error on the current one.
29    fn stack_err(self) -> Self;
30    /// Stack a new error on the current one with a given message.
31    fn stack_err_msg(self, error: impl std::fmt::Display + Send + Sync + 'static) -> Self;
32}
33
34/// Implementation for [`Result`] allows adding error codes on results.
35impl<T, E, C> ErrorStacks<C> for Result<T, E>
36where
37    C: Send + Sync + 'static + Eq + PartialEq + Clone,
38    E: ErrorStacks<C>,
39{
40    fn err_code(&self) -> Option<&C> {
41        self.as_ref().err().and_then(|e| e.err_code())
42    }
43
44    fn with_err_code(self, code: C) -> Self {
45        self.map_err(|e| e.with_err_code(code))
46    }
47
48    fn with_no_err_code(self) -> Self {
49        self.map_err(|e| e.with_no_err_code())
50    }
51
52    fn err_uri(&self) -> Option<&str> {
53        self.as_ref().err().and_then(|e| e.err_uri())
54    }
55
56    fn with_err_uri(self, uri: String) -> Self {
57        self.map_err(|e| e.with_err_uri(uri))
58    }
59
60    fn with_no_err_uri(self) -> Self {
61        self.map_err(|e| e.with_no_err_uri())
62    }
63
64    fn with_err_msg(self, error: impl std::fmt::Display + Send + Sync + 'static) -> Self {
65        self.map_err(|e| e.with_err_msg(error))
66    }
67
68    fn with_no_err_msg(self) -> Self {
69        self.map_err(|e| e.with_no_err_msg())
70    }
71
72    fn stack_err(self) -> Self {
73        self.map_err(|e| e.stack_err())
74    }
75
76    fn stack_err_msg(self, error: impl std::fmt::Display + Send + Sync + 'static) -> Self {
77        self.map_err(|e| e.stack_err_msg(error))
78    }
79}
80
81/// A simple error type that implements the [`ErrorStacks`] trait.
82#[derive(Default)]
83pub struct StackError {
84    message: Option<Box<dyn std::fmt::Display + Send + Sync + 'static>>,
85    source: Option<Box<StackError>>,
86    code: Option<ErrorCode>,
87    uri: Option<String>,
88}
89
90impl StackError {
91    /// Creates a new empty StackError.
92    pub fn new() -> Self {
93        Self::default()
94    }
95
96    /// Creates a new StackError from any error message that implements
97    /// Display + Send + Sync.
98    pub fn from_msg(error: impl std::fmt::Display + Send + Sync + 'static) -> Self {
99        Self {
100            message: Some(Box::new(error)),
101            ..Default::default()
102        }
103    }
104}
105
106impl ErrorStacks<ErrorCode> for StackError {
107    fn err_code(&self) -> Option<&ErrorCode> {
108        self.code.as_ref()
109    }
110
111    fn with_err_code(self, code: ErrorCode) -> Self {
112        Self {
113            code: Some(code),
114            ..self
115        }
116    }
117
118    fn with_no_err_code(self) -> Self {
119        Self { code: None, ..self }
120    }
121
122    fn err_uri(&self) -> Option<&str> {
123        self.uri.as_deref()
124    }
125
126    fn with_err_uri(self, uri: String) -> Self {
127        Self {
128            uri: Some(uri),
129            ..self
130        }
131    }
132
133    fn with_no_err_uri(self) -> Self {
134        Self { uri: None, ..self }
135    }
136
137    fn with_err_msg(self, message: impl std::fmt::Display + Send + Sync + 'static) -> Self {
138        Self {
139            message: Some(Box::new(message)),
140            ..self
141        }
142    }
143
144    fn with_no_err_msg(self) -> Self {
145        Self {
146            message: None,
147            ..self
148        }
149    }
150
151    fn stack_err(self) -> Self {
152        let code = self.code;
153        let uri = self.uri.clone();
154        Self {
155            message: None,
156            source: Some(Box::new(self)),
157            code,
158            uri,
159        }
160    }
161
162    fn stack_err_msg(self, message: impl std::fmt::Display + Send + Sync + 'static) -> Self {
163        let code = self.code;
164        let uri = self.uri.clone();
165        Self {
166            message: Some(Box::new(message)),
167            source: Some(Box::new(self)),
168            code,
169            uri,
170        }
171    }
172}
173
174impl std::fmt::Display for StackError {
175    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
176        match &self.message {
177            Some(error) => {
178                write!(f, "{}", error)
179            }
180            None => Ok(()),
181        }
182    }
183}
184
185impl std::fmt::Debug for StackError {
186    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187        for (idx, err) in std::iter::successors(Some(self), |e| e.source.as_deref())
188            .collect::<Vec<_>>()
189            .into_iter()
190            .rev()
191            .enumerate()
192        {
193            if idx > 0 {
194                writeln!(f)?;
195            }
196            write!(f, "{err}")?;
197        }
198        Ok(())
199    }
200}
201
202impl std::error::Error for StackError {
203    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
204        match &self.source {
205            Some(source) => Some(source.as_ref()),
206            None => None,
207        }
208    }
209}