Skip to main content

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