Skip to main content

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