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(serialize = "E: serde::Serialize", deserialize = "E: serde::de::DeserializeOwned"))]
33pub enum AttemptFailure<E> {
34 /// The operation returned an application error.
35 Error(E),
36
37 /// The attempt exceeded the effective timeout.
38 ///
39 /// This can be the configured per-attempt timeout, the remaining
40 /// max-operation-elapsed budget, or the remaining max-total-elapsed budget
41 /// used by async and worker-thread attempts.
42 Timeout,
43
44 /// The attempt panicked inside an isolated execution boundary.
45 Panic(AttemptPanic),
46
47 /// The retry executor failed before the attempt could run normally.
48 Executor(AttemptExecutorError),
49}
50
51impl<E> AttemptFailure<E> {
52 /// Returns the application error when this failure wraps one.
53 ///
54 /// # Parameters
55 /// This method has no parameters.
56 ///
57 /// # Returns
58 /// `Some(&E)` for [`AttemptFailure::Error`], or `None` for
59 /// runtime-generated failures.
60 ///
61 /// # Errors
62 /// This method does not return errors.
63 #[inline]
64 pub fn as_error(&self) -> Option<&E> {
65 match self {
66 Self::Error(error) => Some(error),
67 Self::Timeout | Self::Panic(_) | Self::Executor(_) => None,
68 }
69 }
70
71 /// Consumes the failure and returns the application error when present.
72 ///
73 /// # Parameters
74 /// This method has no parameters.
75 ///
76 /// # Returns
77 /// `Some(E)` for [`AttemptFailure::Error`], or `None` for
78 /// runtime-generated failures.
79 ///
80 /// # Errors
81 /// This method does not return errors.
82 #[inline]
83 pub fn into_error(self) -> Option<E> {
84 match self {
85 Self::Error(error) => Some(error),
86 Self::Timeout | Self::Panic(_) | Self::Executor(_) => None,
87 }
88 }
89
90 /// Returns captured panic information when this failure wraps one.
91 ///
92 /// # Parameters
93 /// This method has no parameters.
94 ///
95 /// # Returns
96 /// `Some(&AttemptPanic)` for [`AttemptFailure::Panic`], or `None` for other
97 /// variants.
98 ///
99 /// # Errors
100 /// This method does not return errors.
101 #[inline]
102 pub fn as_panic(&self) -> Option<&AttemptPanic> {
103 match self {
104 Self::Panic(panic) => Some(panic),
105 Self::Error(_) | Self::Timeout | Self::Executor(_) => None,
106 }
107 }
108
109 /// Returns executor failure information when this failure wraps one.
110 ///
111 /// # Parameters
112 /// This method has no parameters.
113 ///
114 /// # Returns
115 /// `Some(&AttemptExecutorError)` for [`AttemptFailure::Executor`], or
116 /// `None` for other variants.
117 ///
118 /// # Errors
119 /// This method does not return errors.
120 #[inline]
121 pub fn as_executor_error(&self) -> Option<&AttemptExecutorError> {
122 match self {
123 Self::Executor(error) => Some(error),
124 Self::Error(_) | Self::Timeout | Self::Panic(_) => None,
125 }
126 }
127}
128
129impl<E: fmt::Display> fmt::Display for AttemptFailure<E> {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 match self {
132 Self::Error(error) => write!(f, "{error}"),
133 Self::Timeout => write!(f, "attempt timed out"),
134 Self::Panic(panic) => write!(f, "attempt panicked: {panic}"),
135 Self::Executor(error) => write!(f, "attempt executor failed: {error}"),
136 }
137 }
138}