1#![allow(non_snake_case)]
19
20use std::error::Error as StdError;
45use std::fmt;
46
47#[derive(Debug)]
49pub struct DMSCErrorChain {
50 source: Box<dyn StdError + Send + Sync>,
52 context: String,
54 previous: Option<Box<DMSCErrorChain>>,
56}
57
58impl DMSCErrorChain {
59 pub fn new<E>(source: E) -> Self
61 where
62 E: StdError + Send + Sync + 'static,
63 {
64 Self {
65 source: Box::new(source),
66 context: String::new(),
67 previous: None,
68 }
69 }
70
71 pub fn with_context<E, S>(source: E, context: S) -> Self
73 where
74 E: StdError + Send + Sync + 'static,
75 S: Into<String>,
76 {
77 Self {
78 source: Box::new(source),
79 context: context.into(),
80 previous: None,
81 }
82 }
83
84 pub fn context<S>(mut self, context: S) -> Self
86 where
87 S: Into<String>,
88 {
89 let source = std::mem::replace(&mut self.source, Box::new(std::io::Error::other("error_chain_internal")));
90 Self {
91 source,
92 context: context.into(),
93 previous: Some(Box::new(self)),
94 }
95 }
96
97 pub fn source_error(&self) -> &(dyn StdError + Send + Sync) {
99 &*self.source
100 }
101
102 pub fn get_context(&self) -> &str {
104 &self.context
105 }
106
107 pub fn previous(&self) -> Option<&DMSCErrorChain> {
109 self.previous.as_deref()
110 }
111
112 pub fn chain(&self) -> DMSCErrorChainIter<'_> {
114 DMSCErrorChainIter { current: Some(self) }
115 }
116
117 pub fn contains<E>(&self) -> bool
119 where
120 E: StdError + Send + Sync + 'static,
121 {
122 if self.source.is::<E>() {
123 return true;
124 }
125
126 let mut current = self.previous.as_deref();
127 while let Some(chain) = current {
128 if chain.source.is::<E>() {
129 return true;
130 }
131 current = chain.previous.as_deref();
132 }
133 false
134 }
135
136 pub fn root_cause(&self) -> &(dyn StdError + Send + Sync) {
138 let mut current = self;
139 while let Some(prev) = ¤t.previous {
140 current = prev;
141 }
142 current.source_error()
143 }
144
145 pub fn pretty_format(&self) -> String {
147 let mut result = String::new();
148
149 if !self.context.is_empty() {
150 result.push_str(&format!("Error: {}\n", self.context));
151 }
152 result.push_str(&format!("Source: {}\n", self.source_error()));
153
154 let mut level = 1;
155 let mut current = self.previous.as_deref();
156 while let Some(chain) = current {
157 result.push_str(&format!("\nCaused by (level {level}):\n"));
158 if !chain.context.is_empty() {
159 result.push_str(&format!(" {}\n", chain.get_context()));
160 }
161 result.push_str(&format!(" {}\n", chain.source_error()));
162 level += 1;
163 current = chain.previous.as_deref();
164 }
165
166 result
167 }
168}
169
170impl StdError for DMSCErrorChain {
171 fn source(&self) -> Option<&(dyn StdError + 'static)> {
172 Some(&*self.source)
173 }
174}
175
176impl fmt::Display for DMSCErrorChain {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 if !self.context.is_empty() {
179 write!(f, "{}", self.context)?;
180 if self.previous.is_some() {
181 write!(f, ": ")?;
182 }
183 }
184
185 if let Some(prev) = &self.previous {
186 write!(f, "{prev}")?;
187 } else {
188 write!(f, "{}", self.source_error())?;
189 }
190
191 Ok(())
192 }
193}
194
195pub struct DMSCErrorChainIter<'a> {
197 current: Option<&'a DMSCErrorChain>,
198}
199
200impl<'a> Iterator for DMSCErrorChainIter<'a> {
201 type Item = &'a DMSCErrorChain;
202
203 fn next(&mut self) -> Option<Self::Item> {
204 let current = self.current?;
205 self.current = current.previous.as_deref();
206 Some(current)
207 }
208}
209
210pub trait DMSCErrorContext<T, E> {
212 fn chain_context<S>(self, context: S) -> Result<T, DMSCErrorChain>
214 where
215 S: Into<String>;
216
217 fn with_chain_context<S, F>(self, f: F) -> Result<T, DMSCErrorChain>
219 where
220 S: Into<String>,
221 F: FnOnce() -> S;
222}
223
224impl<T, E> DMSCErrorContext<T, E> for Result<T, E>
225where
226 E: StdError + Send + Sync + 'static,
227{
228 fn chain_context<S>(self, context: S) -> Result<T, DMSCErrorChain>
229 where
230 S: Into<String>,
231 {
232 self.map_err(|e| DMSCErrorChain::with_context(e, context))
233 }
234
235 fn with_chain_context<S, F>(self, f: F) -> Result<T, DMSCErrorChain>
236 where
237 S: Into<String>,
238 F: FnOnce() -> S,
239 {
240 self.map_err(|e| DMSCErrorChain::with_context(e, f()))
241 }
242}
243
244pub trait DMSCOptionErrorContext<T> {
246 fn ok_or_chain<E>(self, err: E) -> Result<T, DMSCErrorChain>
248 where
249 E: StdError + Send + Sync + 'static;
250
251 fn ok_or_else_chain<E, F>(self, f: F) -> Result<T, DMSCErrorChain>
253 where
254 E: StdError + Send + Sync + 'static,
255 F: FnOnce() -> E;
256}
257
258impl<T> DMSCOptionErrorContext<T> for Option<T> {
259 fn ok_or_chain<E>(self, err: E) -> Result<T, DMSCErrorChain>
260 where
261 E: StdError + Send + Sync + 'static,
262 {
263 self.ok_or_else(|| DMSCErrorChain::new(err))
264 }
265
266 fn ok_or_else_chain<E, F>(self, f: F) -> Result<T, DMSCErrorChain>
267 where
268 E: StdError + Send + Sync + 'static,
269 F: FnOnce() -> E,
270 {
271 self.ok_or_else(|| DMSCErrorChain::new(f()))
272 }
273}
274
275pub mod utils {
277 use super::*;
278
279 pub fn chain_from_msg<S>(msg: S) -> DMSCErrorChain
281 where
282 S: Into<String>,
283 {
284 DMSCErrorChain::new(std::io::Error::other(msg.into()))
285 }
286
287 pub fn chain_if<E, F, S>(err: E, predicate: F, context: S) -> DMSCErrorChain
289 where
290 E: StdError + Send + Sync + 'static,
291 F: FnOnce(&E) -> bool,
292 S: Into<String>,
293 {
294 if predicate(&err) {
295 DMSCErrorChain::with_context(err, context)
296 } else {
297 DMSCErrorChain::new(err)
298 }
299 }
300
301 pub fn chain_from_multiple<S>(errors: Vec<Box<dyn StdError + Send + Sync>>, context: S) -> DMSCErrorChain
303 where
304 S: Into<String>,
305 {
306 if errors.is_empty() {
307 return chain_from_msg("No errors provided");
308 }
309
310 if errors.len() == 1 {
311 let error = errors.into_iter().next()
312 .ok_or_else(|| std::io::Error::other("errors vector should have at least one element"))
313 .unwrap_or_else(|_| Box::new(std::io::Error::other("errors vector should have at least one element")));
314 return DMSCErrorChain::with_context(MultiError { errors: vec![error] }, context);
315 }
316
317 let combined = MultiError { errors };
318 DMSCErrorChain::with_context(combined, context)
319 }
320
321 #[derive(Debug)]
322 struct MultiError {
323 errors: Vec<Box<dyn StdError + Send + Sync>>,
324 }
325
326 impl fmt::Display for MultiError {
327 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328 writeln!(f, "Multiple errors occurred ({} total):", self.errors.len())?;
329 for (i, err) in self.errors.iter().enumerate() {
330 writeln!(f, " [{}] {}", i + 1, err)?;
331 }
332 Ok(())
333 }
334 }
335
336 impl StdError for MultiError {
337 fn source(&self) -> Option<&(dyn StdError + 'static)> {
338 self.errors.first().map(|e| &**e as &(dyn StdError + 'static))
339 }
340 }
341}