Skip to main content

qubit_retry/error/
retry_attempt_failure.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! Attempt-level failure values.
10//!
11//! This module distinguishes application errors returned by user operations
12//! from timeout failures generated by the retry runtime itself.
13
14use std::fmt;
15use std::time::Duration;
16
17/// Failure produced by a single attempt.
18///
19/// The generic parameter `E` is the caller's original application error type.
20/// Timeout failures do not contain an `E` value because they are produced by
21/// the retry executor while waiting for an asynchronous attempt.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum RetryAttemptFailure<E> {
24    /// The operation returned an application error.
25    Error(E),
26
27    /// An async attempt exceeded the timeout passed to
28    /// [`crate::RetryExecutor::run_async_with_timeout`].
29    AttemptTimeout {
30        /// Time observed by the retry executor for this attempt.
31        elapsed: Duration,
32        /// Configured timeout for one attempt.
33        timeout: Duration,
34    },
35}
36
37impl<E> RetryAttemptFailure<E> {
38    /// Returns the application error when this failure wraps one.
39    ///
40    /// # Parameters
41    /// This method has no parameters.
42    ///
43    /// # Returns
44    /// `Some(&E)` for [`RetryAttemptFailure::Error`], or `None` for
45    /// [`RetryAttemptFailure::AttemptTimeout`].
46    ///
47    /// # Errors
48    /// This method does not return errors.
49    #[inline]
50    pub fn as_error(&self) -> Option<&E> {
51        match self {
52            Self::Error(error) => Some(error),
53            Self::AttemptTimeout { .. } => None,
54        }
55    }
56
57    /// Consumes the failure and returns the application error when present.
58    ///
59    /// # Parameters
60    /// This method has no parameters.
61    ///
62    /// # Returns
63    /// `Some(E)` for [`RetryAttemptFailure::Error`], or `None` for
64    /// [`RetryAttemptFailure::AttemptTimeout`].
65    ///
66    /// # Errors
67    /// This method does not return errors.
68    #[inline]
69    pub fn into_error(self) -> Option<E> {
70        match self {
71            Self::Error(error) => Some(error),
72            Self::AttemptTimeout { .. } => None,
73        }
74    }
75}
76
77impl<E> fmt::Display for RetryAttemptFailure<E>
78where
79    E: fmt::Display,
80{
81    /// Formats the failure for diagnostics.
82    ///
83    /// # Parameters
84    /// - `f`: Formatter provided by the standard formatting machinery.
85    ///
86    /// # Returns
87    /// `fmt::Result` from the formatter.
88    ///
89    /// # Errors
90    /// Returns a formatting error if the underlying formatter fails.
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        match self {
93            Self::Error(error) => write!(f, "{error}"),
94            Self::AttemptTimeout { elapsed, timeout } => {
95                write!(
96                    f,
97                    "attempt timed out after {elapsed:?}; timeout was {timeout:?}"
98                )
99            }
100        }
101    }
102}