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}