Skip to main content

async_err/
error.rs

1use std::error::Error;
2use std::fmt::{self, Display, Formatter};
3use std::sync::atomic::{AtomicBool, Ordering};
4
5/// Wraps an error with optional context.
6#[derive(Debug)]
7pub struct AsyncError<E: Error + 'static> {
8    error: E,
9    context: Option<String>,
10    hooks_invoked: AtomicBool,
11}
12
13impl<E: Error + 'static> AsyncError<E> {
14    /// Creates a new error wrapper without context.
15    pub fn new(error: E) -> Self {
16        Self {
17            error,
18            context: None,
19            hooks_invoked: AtomicBool::new(false),
20        }
21    }
22
23    /// Adds context to the error.
24    ///
25    /// If the `hooks` feature is enabled, hooks may be triggered.
26    pub fn with_context(mut self, context: String) -> Self {
27        self.context = Some(context);
28        #[cfg(feature = "hooks")]
29        {
30            crate::hooks::invoke_hooks(&self);
31        }
32        self
33    }
34
35    /// Returns a reference to the inner error.
36    pub fn inner_error(&self) -> &E {
37        &self.error
38    }
39
40    /// Returns the context string, if any.
41    pub fn context(&self) -> Option<&str> {
42        self.context.as_deref()
43    }
44
45    /// Returns true if hooks have not been invoked yet, and marks them as invoked.
46    pub fn invoke_hooks_once(&self) -> bool {
47        self.hooks_invoked
48            .compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed)
49            .is_ok()
50    }
51}
52
53impl<E: Error + 'static> Display for AsyncError<E> {
54    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
55        match &self.context {
56            Some(ctx) if !ctx.trim().is_empty() => write!(f, "{}: {}", ctx, self.error),
57            _ => write!(f, "{}", self.error),
58        }
59    }
60}
61
62impl<E: Error + 'static> Error for AsyncError<E> {
63    fn source(&self) -> Option<&(dyn Error + 'static)> {
64        Some(&self.error)
65    }
66}