Skip to main content

error_forge/
async_error_impl.rs

1#[cfg(feature = "async")]
2use async_trait::async_trait;
3use std::error::Error as StdError;
4
5use crate::async_error::AsyncForgeError;
6use crate::error::AppError;
7
8/// `AppError` participates in the `AsyncForgeError` surface so it can
9/// be used wherever async-aware error metadata is required.
10///
11/// All sync metadata methods (`kind`, `caption`, `is_retryable`,
12/// `is_fatal`, `status_code`, `exit_code`, `user_message`,
13/// `dev_message`) delegate to the existing
14/// [`ForgeError`](crate::error::ForgeError) implementation. The
15/// async [`async_handle`](AsyncForgeError::async_handle) method uses
16/// the trait's default no-op implementation — `AppError` has no
17/// default async behaviour beyond carrying its metadata.
18///
19/// # Breaking change from `0.9.x`
20///
21/// `0.9.x` shipped a stub `async_handle` implementation here that
22/// returned `Ok(())` regardless of input but matched on `AppError`
23/// variants as if it were doing something. The stub is removed in
24/// `1.0`; the trait now provides a no-op default and `AppError`
25/// inherits it.
26#[cfg(feature = "async")]
27#[async_trait]
28impl AsyncForgeError for AppError {
29    fn kind(&self) -> &'static str {
30        <Self as crate::error::ForgeError>::kind(self)
31    }
32
33    fn caption(&self) -> &'static str {
34        <Self as crate::error::ForgeError>::caption(self)
35    }
36
37    fn is_retryable(&self) -> bool {
38        <Self as crate::error::ForgeError>::is_retryable(self)
39    }
40
41    fn is_fatal(&self) -> bool {
42        <Self as crate::error::ForgeError>::is_fatal(self)
43    }
44
45    fn status_code(&self) -> u16 {
46        <Self as crate::error::ForgeError>::status_code(self)
47    }
48
49    fn exit_code(&self) -> i32 {
50        <Self as crate::error::ForgeError>::exit_code(self)
51    }
52
53    fn user_message(&self) -> String {
54        <Self as crate::error::ForgeError>::user_message(self)
55    }
56
57    fn dev_message(&self) -> String {
58        <Self as crate::error::ForgeError>::dev_message(self)
59    }
60
61    // `async_handle` uses the trait default (no-op `Ok(())`).
62}
63
64#[cfg(feature = "async")]
65impl AppError {
66    /// Convert an `async` operation's `Result<T, E>` (where `E:
67    /// std::error::Error + Send + Sync + 'static`) into a
68    /// `Result<T, AppError>` by wrapping the error in
69    /// `AppError::other(...)`, marked retryable and non-fatal.
70    ///
71    /// Convenience for the common `async fn -> Result<T, AppError>`
72    /// pattern where the caller wants to flatten any
73    /// `std::error::Error` into an `AppError` shape.
74    pub async fn from_async_result<T, E>(result: Result<T, E>) -> Result<T, Self>
75    where
76        E: StdError + Send + Sync + 'static,
77    {
78        match result {
79            Ok(value) => Ok(value),
80            Err(e) => Err(Self::other(e.to_string())
81                .with_fatal(false)
82                .with_retryable(true)),
83        }
84    }
85
86    /// Run the async hook on this error.
87    ///
88    /// Equivalent to calling
89    /// `<AppError as AsyncForgeError>::async_handle(&self).await`;
90    /// `AppError` does not override the trait default, so this is a
91    /// no-op `Ok(())`. Exists for symmetry with the other
92    /// `*_async` builders on `AppError`.
93    pub async fn handle_async(&self) -> Result<(), Box<dyn StdError + Send + Sync>> {
94        <Self as AsyncForgeError>::async_handle(self).await
95    }
96}