Skip to main content

qubit_dcl/double_checked/
execution_result.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # Execution Result
10//!
11//! Provides the task execution result enum for double-checked locking.
12//!
13//! # Author
14//!
15//! Haixing Hu
16use std::fmt;
17
18use crate::double_checked::executor_error::{CallbackError, ExecutorError};
19
20/// Task execution result
21///
22/// Represents the result of executing a task using an enum to clearly distinguish
23/// between success, unmet conditions, and failure.
24///
25/// # Type Parameters
26///
27/// * `T` - The type of the return value when execution succeeds
28/// * `E` - The type of the error when execution fails
29///
30/// # Examples
31///
32/// ```rust
33/// use qubit_dcl::double_checked::{ExecutionResult, ExecutorError};
34///
35/// let success: ExecutionResult<i32, String> = ExecutionResult::success(42);
36/// if let ExecutionResult::Success(val) = success {
37///     println!("Value: {}", val);
38/// }
39///
40/// let unmet: ExecutionResult<i32, String> = ExecutionResult::unmet();
41///
42/// let failed: ExecutionResult<i32, String> =
43///     ExecutionResult::task_failed("Task failed".to_string());
44/// ```
45///
46/// # Author
47///
48/// Haixing Hu
49#[derive(Debug)]
50pub enum ExecutionResult<T, E> {
51    /// Execution succeeded with a value
52    Success(T),
53
54    /// Double-checked locking condition was not met
55    ConditionNotMet,
56
57    /// Execution failed with an error
58    Failed(ExecutorError<E>),
59}
60
61impl<T, E> ExecutionResult<T, E> {
62    /// Builds [`ExecutionResult::Success`] with `value`.
63    ///
64    /// # Parameters
65    ///
66    /// * `value` - Successful task value.
67    ///
68    /// # Returns
69    ///
70    /// A success result containing `value`.
71    #[inline]
72    pub fn success(value: T) -> Self {
73        ExecutionResult::Success(value)
74    }
75
76    /// Builds [`ExecutionResult::ConditionNotMet`].
77    ///
78    /// # Returns
79    ///
80    /// A result representing a failed double-check condition.
81    #[inline]
82    pub fn unmet() -> Self {
83        ExecutionResult::ConditionNotMet
84    }
85
86    /// Builds a failed result with [`ExecutorError::TaskFailed`].
87    ///
88    /// # Parameters
89    ///
90    /// * `err` - Error returned by the executed task.
91    ///
92    /// # Returns
93    ///
94    /// A failed result wrapping the task error.
95    #[inline]
96    pub fn task_failed(err: E) -> Self {
97        ExecutionResult::Failed(ExecutorError::TaskFailed(err))
98    }
99
100    /// Builds a failed result with [`ExecutorError::PrepareFailed`].
101    ///
102    /// Accepts any [`std::fmt::Display`] value (including [`std::error::Error`] and [`String`]);
103    /// the message is stored in a [`CallbackError`] wrapper.
104    ///
105    /// # Parameters
106    ///
107    /// * `msg` - Prepare error message or displayable error value.
108    ///
109    /// # Returns
110    ///
111    /// A failed result containing the prepare failure message.
112    #[inline]
113    pub fn prepare_failed(msg: impl fmt::Display) -> Self {
114        ExecutionResult::Failed(ExecutorError::PrepareFailed(CallbackError::from_display(
115            msg,
116        )))
117    }
118
119    /// Builds a failed result with [`ExecutorError::PrepareCommitFailed`].
120    ///
121    /// # Parameters
122    ///
123    /// * `msg` - Commit error message or displayable error value.
124    ///
125    /// # Returns
126    ///
127    /// A failed result containing the prepare-commit failure message.
128    #[inline]
129    pub fn prepare_commit_failed(msg: impl fmt::Display) -> Self {
130        ExecutionResult::Failed(ExecutorError::PrepareCommitFailed(
131            CallbackError::from_display(msg),
132        ))
133    }
134
135    /// Builds a failed result with [`ExecutorError::PrepareFailed`] and explicit
136    /// callback type metadata.
137    ///
138    /// The callback type can later be read from
139    /// [`ExecutorError::callback_type`].
140    ///
141    /// # Parameters
142    ///
143    /// * `callback_type` - Callback type tag, e.g. `"prepare"`.
144    /// * `msg` - Failure message.
145    #[inline]
146    pub fn prepare_failed_with_type(
147        callback_type: &'static str,
148        msg: impl std::fmt::Display,
149    ) -> Self {
150        ExecutionResult::Failed(ExecutorError::PrepareFailed(CallbackError::with_type(
151            callback_type,
152            msg,
153        )))
154    }
155
156    /// Builds a failed result with [`ExecutorError::PrepareRollbackFailed`].
157    ///
158    /// # Parameters
159    ///
160    /// * `original` - Original failure that triggered prepare rollback.
161    /// * `rollback` - Failure produced by the rollback action.
162    ///
163    /// # Returns
164    ///
165    /// A failed result containing both original and rollback messages.
166    #[inline]
167    pub fn prepare_rollback_failed(
168        original: impl Into<String>,
169        rollback: impl Into<String>,
170    ) -> Self {
171        ExecutionResult::Failed(ExecutorError::PrepareRollbackFailed {
172            original: CallbackError::from_display(original.into()),
173            rollback: CallbackError::from_display(rollback.into()),
174        })
175    }
176
177    /// Wraps an arbitrary [`ExecutorError`] as [`ExecutionResult::Failed`].
178    ///
179    /// # Parameters
180    ///
181    /// * `err` - Executor error to store in the failed result.
182    ///
183    /// # Returns
184    ///
185    /// A failed result containing `err`.
186    #[inline]
187    pub fn from_executor_error(err: ExecutorError<E>) -> Self {
188        ExecutionResult::Failed(err)
189    }
190
191    /// Checks if the execution was successful.
192    ///
193    /// # Returns
194    ///
195    /// `true` if this result is [`ExecutionResult::Success`].
196    #[inline]
197    pub fn is_success(&self) -> bool {
198        matches!(self, ExecutionResult::Success(_))
199    }
200
201    /// Checks if the condition was not met.
202    ///
203    /// # Returns
204    ///
205    /// `true` if this result is [`ExecutionResult::ConditionNotMet`].
206    #[inline]
207    pub fn is_unmet(&self) -> bool {
208        matches!(self, ExecutionResult::ConditionNotMet)
209    }
210
211    /// Checks if the execution failed.
212    ///
213    /// # Returns
214    ///
215    /// `true` if this result is [`ExecutionResult::Failed`].
216    #[inline]
217    pub fn is_failed(&self) -> bool {
218        matches!(self, ExecutionResult::Failed(_))
219    }
220
221    /// Converts the result to a standard Result
222    ///
223    /// # Returns
224    ///
225    /// * `Ok(Some(T))` - If execution was successful
226    /// * `Ok(None)` - If condition was not met
227    /// * `Err(ExecutorError<E>)` - If execution failed
228    ///
229    /// # Errors
230    ///
231    /// Returns the stored [`ExecutorError`] when this value is
232    /// [`ExecutionResult::Failed`].
233    #[inline]
234    pub fn into_result(self) -> Result<Option<T>, ExecutorError<E>> {
235        match self {
236            ExecutionResult::Success(v) => Ok(Some(v)),
237            ExecutionResult::ConditionNotMet => Ok(None),
238            ExecutionResult::Failed(e) => Err(e),
239        }
240    }
241}
242
243impl<T, E> ExecutionResult<T, E>
244where
245    E: fmt::Display,
246{
247    /// Unwraps the success value, panicking if not successful
248    ///
249    /// # Returns
250    ///
251    /// The success value stored in [`ExecutionResult::Success`].
252    ///
253    /// # Panics
254    ///
255    /// Panics if this result is [`ExecutionResult::ConditionNotMet`] or
256    /// [`ExecutionResult::Failed`].
257    #[inline]
258    pub fn unwrap(self) -> T {
259        match self {
260            ExecutionResult::Success(v) => v,
261            ExecutionResult::ConditionNotMet => {
262                panic!("Called unwrap on ExecutionResult::ConditionNotMet")
263            }
264            ExecutionResult::Failed(e) => {
265                panic!("Called unwrap on ExecutionResult::Failed: {}", e)
266            }
267        }
268    }
269}