qubit_cas/error/cas_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 CAS failures.
10
11use std::fmt;
12use std::sync::Arc;
13
14use super::cas_attempt_failure_kind::CasAttemptFailureKind;
15
16/// Failure produced by one CAS attempt.
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum CasAttemptFailure<T, E> {
19 /// Compare-and-swap failed because another writer changed the state first.
20 Conflict {
21 /// State observed after the conflict.
22 current: Arc<T>,
23 },
24
25 /// Business logic requested another attempt.
26 Retry {
27 /// Current state for the failed attempt.
28 current: Arc<T>,
29 /// Retryable business error.
30 error: E,
31 },
32
33 /// Business logic aborted the CAS flow.
34 Abort {
35 /// Current state for the failed attempt.
36 current: Arc<T>,
37 /// Terminal business error.
38 error: E,
39 },
40
41 /// One async attempt exceeded the configured timeout.
42 Timeout {
43 /// State snapshot used by the timed-out attempt.
44 current: Arc<T>,
45 },
46}
47
48impl<T, E> CasAttemptFailure<T, E> {
49 /// Creates a conflict failure.
50 ///
51 /// # Parameters
52 /// - `current`: Current state observed after the conflict.
53 ///
54 /// # Returns
55 /// A [`CasAttemptFailure::Conflict`] value.
56 #[inline]
57 pub(crate) fn conflict(current: Arc<T>) -> Self {
58 Self::Conflict { current }
59 }
60
61 /// Creates a retryable business failure.
62 ///
63 /// # Parameters
64 /// - `current`: Current state for the failed attempt.
65 /// - `error`: Retryable business error.
66 ///
67 /// # Returns
68 /// A [`CasAttemptFailure::Retry`] value.
69 #[inline]
70 pub(crate) fn retry(current: Arc<T>, error: E) -> Self {
71 Self::Retry { current, error }
72 }
73
74 /// Creates an abort failure.
75 ///
76 /// # Parameters
77 /// - `current`: Current state for the failed attempt.
78 /// - `error`: Terminal business error.
79 ///
80 /// # Returns
81 /// A [`CasAttemptFailure::Abort`] value.
82 #[inline]
83 pub(crate) fn abort(current: Arc<T>, error: E) -> Self {
84 Self::Abort { current, error }
85 }
86
87 /// Creates a timeout failure.
88 ///
89 /// # Parameters
90 /// - `current`: State snapshot used by the timed-out attempt.
91 ///
92 /// # Returns
93 /// A [`CasAttemptFailure::Timeout`] value.
94 #[inline]
95 #[cfg(feature = "tokio")]
96 pub(crate) fn timeout(current: Arc<T>) -> Self {
97 Self::Timeout { current }
98 }
99
100 /// Returns the state snapshot associated with this failure.
101 ///
102 /// # Returns
103 /// Shared reference to the current state.
104 #[inline]
105 pub fn current(&self) -> &Arc<T> {
106 match self {
107 Self::Conflict { current }
108 | Self::Retry { current, .. }
109 | Self::Abort { current, .. }
110 | Self::Timeout { current } => current,
111 }
112 }
113
114 /// Returns the lightweight kind of this attempt failure.
115 ///
116 /// # Returns
117 /// The [`CasAttemptFailureKind`] matching this failure variant.
118 #[inline]
119 pub fn kind(&self) -> CasAttemptFailureKind {
120 match self {
121 Self::Conflict { .. } => CasAttemptFailureKind::Conflict,
122 Self::Retry { .. } => CasAttemptFailureKind::Retry,
123 Self::Abort { .. } => CasAttemptFailureKind::Abort,
124 Self::Timeout { .. } => CasAttemptFailureKind::Timeout,
125 }
126 }
127
128 /// Returns the business error when this failure carries one.
129 ///
130 /// # Returns
131 /// `Some(&E)` for [`CasAttemptFailure::Retry`] and
132 /// [`CasAttemptFailure::Abort`], or `None` otherwise.
133 #[inline]
134 pub fn error(&self) -> Option<&E> {
135 match self {
136 Self::Retry { error, .. } | Self::Abort { error, .. } => Some(error),
137 Self::Conflict { .. } | Self::Timeout { .. } => None,
138 }
139 }
140
141 /// Returns whether this failure is a compare-and-swap conflict.
142 ///
143 /// # Returns
144 /// `true` for [`CasAttemptFailure::Conflict`].
145 #[inline]
146 pub fn is_conflict(&self) -> bool {
147 matches!(self, Self::Conflict { .. })
148 }
149
150 /// Returns whether this failure is a retryable business error.
151 ///
152 /// # Returns
153 /// `true` for [`CasAttemptFailure::Retry`].
154 #[inline]
155 pub fn is_retry(&self) -> bool {
156 matches!(self, Self::Retry { .. })
157 }
158
159 /// Returns whether this failure aborts the CAS flow.
160 ///
161 /// # Returns
162 /// `true` for [`CasAttemptFailure::Abort`].
163 #[inline]
164 pub fn is_abort(&self) -> bool {
165 matches!(self, Self::Abort { .. })
166 }
167
168 /// Returns whether this failure came from an async timeout.
169 ///
170 /// # Returns
171 /// `true` for [`CasAttemptFailure::Timeout`].
172 #[inline]
173 pub fn is_timeout(&self) -> bool {
174 matches!(self, Self::Timeout { .. })
175 }
176}
177
178impl<T, E> fmt::Display for CasAttemptFailure<T, E>
179where
180 E: fmt::Display,
181{
182 /// Formats the attempt failure for diagnostics.
183 ///
184 /// # Parameters
185 /// - `f`: Formatter provided by the standard formatting machinery.
186 ///
187 /// # Returns
188 /// `fmt::Result` from the formatter.
189 ///
190 /// # Errors
191 /// Returns a formatting error if the formatter fails.
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 match self {
194 Self::Conflict { .. } => write!(f, "compare-and-swap conflict"),
195 Self::Retry { error, .. } => write!(f, "retryable CAS failure: {error}"),
196 Self::Abort { error, .. } => write!(f, "aborted CAS failure: {error}"),
197 Self::Timeout { .. } => write!(f, "CAS attempt timed out"),
198 }
199 }
200}