qubit_retry/error/attempt_failure.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Attempt-level failure values.
11//!
12//! A retry failure describes why one operation attempt did not produce a
13//! successful result. It is distinct from [`crate::RetryError`], which describes
14//! why the whole retry flow stopped.
15
16use std::fmt;
17
18use serde::{
19 Deserialize,
20 Serialize,
21};
22
23use super::attempt_executor_error::AttemptExecutorError;
24use super::attempt_panic::AttemptPanic;
25
26/// Failure produced by a single operation attempt.
27///
28/// The generic parameter `E` is the caller's operation error type. Timeout,
29/// panic, and executor failures do not contain `E` because they are generated
30/// by the retry runtime, not returned by the operation.
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
32#[serde(bound(
33 serialize = "E: serde::Serialize",
34 deserialize = "E: serde::de::DeserializeOwned"
35))]
36pub enum AttemptFailure<E> {
37 /// The operation returned an application error.
38 Error(E),
39
40 /// The attempt exceeded the effective timeout.
41 ///
42 /// This can be the configured per-attempt timeout, the remaining
43 /// max-operation-elapsed budget, or the remaining max-total-elapsed budget
44 /// used by async and worker-thread attempts.
45 Timeout,
46
47 /// The attempt panicked inside an isolated execution boundary.
48 Panic(AttemptPanic),
49
50 /// The retry executor failed before the attempt could run normally.
51 Executor(AttemptExecutorError),
52}
53
54impl<E> AttemptFailure<E> {
55 /// Returns the application error when this failure wraps one.
56 ///
57 /// # Parameters
58 /// This method has no parameters.
59 ///
60 /// # Returns
61 /// `Some(&E)` for [`AttemptFailure::Error`], or `None` for
62 /// runtime-generated failures.
63 ///
64 /// # Errors
65 /// This method does not return errors.
66 #[inline]
67 pub fn as_error(&self) -> Option<&E> {
68 match self {
69 Self::Error(error) => Some(error),
70 Self::Timeout | Self::Panic(_) | Self::Executor(_) => None,
71 }
72 }
73
74 /// Consumes the failure and returns the application error when present.
75 ///
76 /// # Parameters
77 /// This method has no parameters.
78 ///
79 /// # Returns
80 /// `Some(E)` for [`AttemptFailure::Error`], or `None` for
81 /// runtime-generated failures.
82 ///
83 /// # Errors
84 /// This method does not return errors.
85 #[inline]
86 pub fn into_error(self) -> Option<E> {
87 match self {
88 Self::Error(error) => Some(error),
89 Self::Timeout | Self::Panic(_) | Self::Executor(_) => None,
90 }
91 }
92
93 /// Returns captured panic information when this failure wraps one.
94 ///
95 /// # Parameters
96 /// This method has no parameters.
97 ///
98 /// # Returns
99 /// `Some(&AttemptPanic)` for [`AttemptFailure::Panic`], or `None` for other
100 /// variants.
101 ///
102 /// # Errors
103 /// This method does not return errors.
104 #[inline]
105 pub fn as_panic(&self) -> Option<&AttemptPanic> {
106 match self {
107 Self::Panic(panic) => Some(panic),
108 Self::Error(_) | Self::Timeout | Self::Executor(_) => None,
109 }
110 }
111
112 /// Returns executor failure information when this failure wraps one.
113 ///
114 /// # Parameters
115 /// This method has no parameters.
116 ///
117 /// # Returns
118 /// `Some(&AttemptExecutorError)` for [`AttemptFailure::Executor`], or
119 /// `None` for other variants.
120 ///
121 /// # Errors
122 /// This method does not return errors.
123 #[inline]
124 pub fn as_executor_error(&self) -> Option<&AttemptExecutorError> {
125 match self {
126 Self::Executor(error) => Some(error),
127 Self::Error(_) | Self::Timeout | Self::Panic(_) => None,
128 }
129 }
130}
131
132impl<E: fmt::Display> fmt::Display for AttemptFailure<E> {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 match self {
135 Self::Error(error) => write!(f, "{error}"),
136 Self::Timeout => write!(f, "attempt timed out"),
137 Self::Panic(panic) => write!(f, "attempt panicked: {panic}"),
138 Self::Executor(error) => write!(f, "attempt executor failed: {error}"),
139 }
140 }
141}