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