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}