Skip to main content

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}