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(
113            msg,
114        )))
115    }
116
117    /// Builds a failed result with [`ExecutorError::PrepareCommitFailed`].
118    ///
119    /// # Parameters
120    ///
121    /// * `msg` - Commit error message or displayable error value.
122    ///
123    /// # Returns
124    ///
125    /// A failed result containing the prepare-commit failure message.
126    #[inline]
127    pub fn prepare_commit_failed(msg: impl fmt::Display) -> Self {
128        ExecutionResult::Failed(ExecutorError::PrepareCommitFailed(
129            CallbackError::from_display(msg),
130        ))
131    }
132
133    /// Builds a failed result with [`ExecutorError::PrepareFailed`] and explicit
134    /// callback type metadata.
135    ///
136    /// The callback type can later be read from
137    /// [`ExecutorError::callback_type`].
138    ///
139    /// # Parameters
140    ///
141    /// * `callback_type` - Callback type tag, e.g. `"prepare"`.
142    /// * `msg` - Failure message.
143    #[inline]
144    pub fn prepare_failed_with_type(
145        callback_type: &'static str,
146        msg: impl std::fmt::Display,
147    ) -> Self {
148        ExecutionResult::Failed(ExecutorError::PrepareFailed(CallbackError::with_type(
149            callback_type,
150            msg,
151        )))
152    }
153
154    /// Builds a failed result with [`ExecutorError::PrepareRollbackFailed`].
155    ///
156    /// # Parameters
157    ///
158    /// * `original` - Original failure that triggered prepare rollback.
159    /// * `rollback` - Failure produced by the rollback action.
160    ///
161    /// # Returns
162    ///
163    /// A failed result containing both original and rollback messages.
164    #[inline]
165    pub fn prepare_rollback_failed(
166        original: impl Into<String>,
167        rollback: impl Into<String>,
168    ) -> Self {
169        ExecutionResult::Failed(ExecutorError::PrepareRollbackFailed {
170            original: CallbackError::from_display(original.into()),
171            rollback: CallbackError::from_display(rollback.into()),
172        })
173    }
174
175    /// Wraps an arbitrary [`ExecutorError`] as [`ExecutionResult::Failed`].
176    ///
177    /// # Parameters
178    ///
179    /// * `err` - Executor error to store in the failed result.
180    ///
181    /// # Returns
182    ///
183    /// A failed result containing `err`.
184    #[inline]
185    pub fn from_executor_error(err: ExecutorError<E>) -> Self {
186        ExecutionResult::Failed(err)
187    }
188
189    /// Checks if the execution was successful.
190    ///
191    /// # Returns
192    ///
193    /// `true` if this result is [`ExecutionResult::Success`].
194    #[inline]
195    pub fn is_success(&self) -> bool {
196        matches!(self, ExecutionResult::Success(_))
197    }
198
199    /// Checks if the condition was not met.
200    ///
201    /// # Returns
202    ///
203    /// `true` if this result is [`ExecutionResult::ConditionNotMet`].
204    #[inline]
205    pub fn is_unmet(&self) -> bool {
206        matches!(self, ExecutionResult::ConditionNotMet)
207    }
208
209    /// Checks if the execution failed.
210    ///
211    /// # Returns
212    ///
213    /// `true` if this result is [`ExecutionResult::Failed`].
214    #[inline]
215    pub fn is_failed(&self) -> bool {
216        matches!(self, ExecutionResult::Failed(_))
217    }
218
219    /// Converts the result to a standard Result
220    ///
221    /// # Returns
222    ///
223    /// * `Ok(Some(T))` - If execution was successful
224    /// * `Ok(None)` - If condition was not met
225    /// * `Err(ExecutorError<E>)` - If execution failed
226    ///
227    /// # Errors
228    ///
229    /// Returns the stored [`ExecutorError`] when this value is
230    /// [`ExecutionResult::Failed`].
231    #[inline]
232    pub fn into_result(self) -> Result<Option<T>, ExecutorError<E>> {
233        match self {
234            ExecutionResult::Success(v) => Ok(Some(v)),
235            ExecutionResult::ConditionNotMet => Ok(None),
236            ExecutionResult::Failed(e) => Err(e),
237        }
238    }
239}
240
241impl<T, E> ExecutionResult<T, E>
242where
243    E: fmt::Display,
244{
245    /// Unwraps the success value, panicking if not successful
246    ///
247    /// # Returns
248    ///
249    /// The success value stored in [`ExecutionResult::Success`].
250    ///
251    /// # Panics
252    ///
253    /// Panics if this result is [`ExecutionResult::ConditionNotMet`] or
254    /// [`ExecutionResult::Failed`].
255    #[inline]
256    pub fn unwrap(self) -> T {
257        match self {
258            ExecutionResult::Success(v) => v,
259            ExecutionResult::ConditionNotMet => {
260                panic!("Called unwrap on ExecutionResult::ConditionNotMet")
261            }
262            ExecutionResult::Failed(e) => {
263                panic!("Called unwrap on ExecutionResult::Failed: {}", e)
264            }
265        }
266    }
267}