Skip to main content

qubit_dcl/double_checked/
executor_ready_builder.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//! Ready builder for [`super::DoubleCheckedLockExecutor`] (tester set, optional
11//! prepare hooks).
12//!
13
14use std::{
15    fmt::Display,
16    marker::PhantomData,
17};
18
19use qubit_function::{
20    ArcRunnable,
21    ArcTester,
22    Runnable,
23};
24
25use super::{
26    CallbackError,
27    ExecutionLogger,
28    double_checked_lock_executor::DoubleCheckedLockExecutor,
29};
30use crate::lock::Lock;
31
32/// Builder state after the required condition tester has been configured.
33///
34/// This state can configure prepare lifecycle callbacks and build the final
35/// [`DoubleCheckedLockExecutor`].
36///
37/// # Type Parameters
38///
39/// * `L` - The lock type implementing [`Lock<T>`].
40/// * `T` - The data type protected by the lock.
41///
42#[derive(Clone)]
43pub struct ExecutorReadyBuilder<L, T> {
44    /// The lock to store in the executor.
45    pub(in crate::double_checked) lock: L,
46
47    /// Required condition tester.
48    pub(in crate::double_checked) tester: ArcTester,
49
50    /// Logger used by the executor.
51    pub(in crate::double_checked) logger: ExecutionLogger,
52
53    /// Optional action executed after the first check and before locking.
54    pub(in crate::double_checked) prepare_action: Option<ArcRunnable<CallbackError>>,
55
56    /// Optional action executed when prepare must be rolled back.
57    pub(in crate::double_checked) rollback_prepare_action: Option<ArcRunnable<CallbackError>>,
58
59    /// Optional action executed when prepare should be committed.
60    pub(in crate::double_checked) commit_prepare_action: Option<ArcRunnable<CallbackError>>,
61
62    /// Whether panics from tester, callbacks, and task are converted to errors.
63    pub(in crate::double_checked) catch_panics: bool,
64
65    /// Carries the protected data type.
66    pub(in crate::double_checked) _phantom: PhantomData<fn() -> T>,
67}
68
69impl<L, T> ExecutorReadyBuilder<L, T>
70where
71    L: Lock<T>,
72{
73    /// Configures logging when the double-checked condition is not met.
74    #[inline]
75    pub fn log_unmet_condition(mut self, level: log::Level, message: impl Into<String>) -> Self {
76        self.logger.set_unmet_condition(Some(level), message);
77        self
78    }
79
80    /// Disables logging when the double-checked condition is not met.
81    #[inline]
82    pub fn disable_unmet_condition_logging(mut self) -> Self {
83        self.logger.disable_unmet_condition();
84        self
85    }
86
87    /// Configures logging when the prepare action fails.
88    #[inline]
89    pub fn log_prepare_failure(
90        mut self,
91        level: log::Level,
92        message_prefix: impl Into<String>,
93    ) -> Self {
94        self.logger.set_prepare_failure(Some(level), message_prefix);
95        self
96    }
97
98    /// Disables logging when the prepare action fails.
99    #[inline]
100    pub fn disable_prepare_failure_logging(mut self) -> Self {
101        self.logger.disable_prepare_failure();
102        self
103    }
104
105    /// Configures logging when the prepare commit action fails.
106    #[inline]
107    pub fn log_prepare_commit_failure(
108        mut self,
109        level: log::Level,
110        message_prefix: impl Into<String>,
111    ) -> Self {
112        self.logger
113            .set_prepare_commit_failure(Some(level), message_prefix);
114        self
115    }
116
117    /// Disables logging when the prepare commit action fails.
118    #[inline]
119    pub fn disable_prepare_commit_failure_logging(mut self) -> Self {
120        self.logger.disable_prepare_commit_failure();
121        self
122    }
123
124    /// Configures logging when the prepare rollback action fails.
125    #[inline]
126    pub fn log_prepare_rollback_failure(
127        mut self,
128        level: log::Level,
129        message_prefix: impl Into<String>,
130    ) -> Self {
131        self.logger
132            .set_prepare_rollback_failure(Some(level), message_prefix);
133        self
134    }
135
136    /// Disables logging when the prepare rollback action fails.
137    #[inline]
138    pub fn disable_prepare_rollback_failure_logging(mut self) -> Self {
139        self.logger.disable_prepare_rollback_failure();
140        self
141    }
142
143    /// Sets the prepare action.
144    ///
145    /// The action runs after the first condition check succeeds and before the
146    /// lock is acquired. If it succeeds, the executor will later run either
147    /// rollback or commit according to the final task result.
148    ///
149    /// Errors returned by this action are converted to [`String`] and reported
150    /// by execution methods as [`super::ExecutionResult::Failed`].
151    ///
152    /// # Parameters
153    ///
154    /// * `prepare_action` - The fallible action to run before locking.
155    ///
156    /// # Returns
157    ///
158    /// This builder with prepare configured.
159    #[inline]
160    pub fn prepare<Rn, E>(mut self, prepare_action: Rn) -> Self
161    where
162        Rn: Runnable<E> + Send + 'static,
163        E: Display,
164    {
165        let mut action = prepare_action;
166        self.prepare_action = Some(ArcRunnable::new(move || {
167            action
168                .run()
169                .map_err(|error| CallbackError::with_type("prepare", error))
170        }));
171        self
172    }
173
174    /// Sets the rollback action for a successfully completed prepare action.
175    ///
176    /// Errors returned by this action are converted to [`String`] and replace
177    /// the original execution result with a prepare-rollback failure.
178    ///
179    /// # Parameters
180    ///
181    /// * `rollback_prepare_action` - The action to run if the second condition
182    ///   check or task execution fails after prepare succeeds.
183    ///
184    /// # Returns
185    ///
186    /// This builder with prepare rollback configured.
187    #[inline]
188    pub fn rollback_prepare<Rn, E>(mut self, rollback_prepare_action: Rn) -> Self
189    where
190        Rn: Runnable<E> + Send + 'static,
191        E: Display,
192    {
193        let mut action = rollback_prepare_action;
194        self.rollback_prepare_action = Some(ArcRunnable::new(move || {
195            action
196                .run()
197                .map_err(|error| CallbackError::with_type("prepare_rollback", error))
198        }));
199        self
200    }
201
202    /// Sets the commit action for a successfully completed prepare action.
203    ///
204    /// Errors returned by this action are converted to [`String`] and replace
205    /// an otherwise successful execution result with a prepare-commit failure.
206    ///
207    /// # Parameters
208    ///
209    /// * `commit_prepare_action` - The action to run if the task succeeds after
210    ///   prepare succeeds.
211    ///
212    /// # Returns
213    ///
214    /// This builder with prepare commit configured.
215    #[inline]
216    pub fn commit_prepare<Rn, E>(mut self, commit_prepare_action: Rn) -> Self
217    where
218        Rn: Runnable<E> + Send + 'static,
219        E: Display,
220    {
221        let mut action = commit_prepare_action;
222        self.commit_prepare_action = Some(ArcRunnable::new(move || {
223            action
224                .run()
225                .map_err(|error| CallbackError::with_type("prepare_commit", error))
226        }));
227        self
228    }
229
230    /// Enables panic capture for tester, prepare callbacks, and task execution.
231    ///
232    /// When enabled, panic payloads are converted to
233    /// [`super::executor_error::ExecutorError::Panic`] and surfaced through
234    /// [`super::ExecutionResult`].
235    #[inline]
236    pub fn catch_panics(mut self) -> Self {
237        self.catch_panics = true;
238        self
239    }
240
241    /// Sets whether panic capture for tester, prepare callbacks, and task
242    /// execution is enabled.
243    #[inline]
244    pub fn set_catch_panics(mut self, catch_panics: bool) -> Self {
245        self.catch_panics = catch_panics;
246        self
247    }
248
249    /// Disables panic capture for tester, prepare callbacks, and task execution.
250    #[inline]
251    pub fn disable_catch_panics(mut self) -> Self {
252        self.catch_panics = false;
253        self
254    }
255
256    /// Builds the reusable executor.
257    ///
258    /// # Returns
259    ///
260    /// A [`DoubleCheckedLockExecutor`] containing the configured lock, tester,
261    /// execution logger, and prepare lifecycle callbacks.
262    #[inline]
263    pub fn build(self) -> DoubleCheckedLockExecutor<L, T> {
264        DoubleCheckedLockExecutor::new(self)
265    }
266}