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}