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    ///
75    /// # Parameters
76    ///
77    /// * `level` - Log level used for unmet-condition messages.
78    /// * `message` - Full log message emitted when the condition is not met.
79    ///
80    /// # Returns
81    ///
82    /// This builder with unmet-condition logging configured.
83    #[inline]
84    pub fn log_unmet_condition(mut self, level: log::Level, message: impl Into<String>) -> Self {
85        self.logger.set_unmet_condition(Some(level), message);
86        self
87    }
88
89    /// Disables logging when the double-checked condition is not met.
90    ///
91    /// # Returns
92    ///
93    /// This builder with unmet-condition logging disabled.
94    #[inline]
95    pub fn disable_unmet_condition_logging(mut self) -> Self {
96        self.logger.disable_unmet_condition();
97        self
98    }
99
100    /// Configures logging when the prepare action fails.
101    ///
102    /// # Parameters
103    ///
104    /// * `level` - Log level used for prepare failure messages.
105    /// * `message_prefix` - Prefix placed before the prepare failure text.
106    ///
107    /// # Returns
108    ///
109    /// This builder with prepare failure logging configured.
110    #[inline]
111    pub fn log_prepare_failure(
112        mut self,
113        level: log::Level,
114        message_prefix: impl Into<String>,
115    ) -> Self {
116        self.logger.set_prepare_failure(Some(level), message_prefix);
117        self
118    }
119
120    /// Disables logging when the prepare action fails.
121    ///
122    /// # Returns
123    ///
124    /// This builder with prepare failure logging disabled.
125    #[inline]
126    pub fn disable_prepare_failure_logging(mut self) -> Self {
127        self.logger.disable_prepare_failure();
128        self
129    }
130
131    /// Configures logging when the prepare commit action fails.
132    ///
133    /// # Parameters
134    ///
135    /// * `level` - Log level used for prepare-commit failure messages.
136    /// * `message_prefix` - Prefix placed before the prepare-commit failure
137    ///   text.
138    ///
139    /// # Returns
140    ///
141    /// This builder with prepare-commit failure logging configured.
142    #[inline]
143    pub fn log_prepare_commit_failure(
144        mut self,
145        level: log::Level,
146        message_prefix: impl Into<String>,
147    ) -> Self {
148        self.logger
149            .set_prepare_commit_failure(Some(level), message_prefix);
150        self
151    }
152
153    /// Disables logging when the prepare commit action fails.
154    ///
155    /// # Returns
156    ///
157    /// This builder with prepare-commit failure logging disabled.
158    #[inline]
159    pub fn disable_prepare_commit_failure_logging(mut self) -> Self {
160        self.logger.disable_prepare_commit_failure();
161        self
162    }
163
164    /// Configures logging when the prepare rollback action fails.
165    ///
166    /// # Parameters
167    ///
168    /// * `level` - Log level used for prepare-rollback failure messages.
169    /// * `message_prefix` - Prefix placed before the prepare-rollback failure
170    ///   text.
171    ///
172    /// # Returns
173    ///
174    /// This builder with prepare-rollback failure logging configured.
175    #[inline]
176    pub fn log_prepare_rollback_failure(
177        mut self,
178        level: log::Level,
179        message_prefix: impl Into<String>,
180    ) -> Self {
181        self.logger
182            .set_prepare_rollback_failure(Some(level), message_prefix);
183        self
184    }
185
186    /// Disables logging when the prepare rollback action fails.
187    ///
188    /// # Returns
189    ///
190    /// This builder with prepare-rollback failure logging disabled.
191    #[inline]
192    pub fn disable_prepare_rollback_failure_logging(mut self) -> Self {
193        self.logger.disable_prepare_rollback_failure();
194        self
195    }
196
197    /// Sets the prepare action.
198    ///
199    /// The action runs after the first condition check succeeds and before the
200    /// lock is acquired. If it succeeds, the executor will later run either
201    /// rollback or commit according to the final task result.
202    ///
203    /// Errors returned by this action are converted to [`String`] and reported
204    /// by execution methods as [`super::ExecutionResult::Failed`].
205    ///
206    /// # Parameters
207    ///
208    /// * `prepare_action` - The fallible action to run before locking.
209    ///
210    /// # Returns
211    ///
212    /// This builder with prepare configured.
213    ///
214    /// # Errors
215    ///
216    /// This builder method does not return errors. If `prepare_action` later
217    /// returns an error during execution, the execution result becomes
218    /// [`super::ExecutionResult::Failed`] with
219    /// [`super::ExecutorError::PrepareFailed`].
220    #[inline]
221    pub fn prepare<Rn, E>(mut self, prepare_action: Rn) -> Self
222    where
223        Rn: Runnable<E> + Send + 'static,
224        E: Display,
225    {
226        let mut action = prepare_action;
227        self.prepare_action = Some(ArcRunnable::new(move || {
228            action
229                .run()
230                .map_err(|error| CallbackError::with_callback_type("prepare", error))
231        }));
232        self
233    }
234
235    /// Sets the rollback action for a successfully completed prepare action.
236    ///
237    /// Errors returned by this action are converted to [`String`] and replace
238    /// the original execution result with a prepare-rollback failure.
239    ///
240    /// # Parameters
241    ///
242    /// * `rollback_prepare_action` - The action to run if the second condition
243    ///   check or task execution fails after prepare succeeds.
244    ///
245    /// # Returns
246    ///
247    /// This builder with prepare rollback configured.
248    ///
249    /// # Errors
250    ///
251    /// This builder method does not return errors. If
252    /// `rollback_prepare_action` later returns an error during execution, the
253    /// execution result becomes [`super::ExecutionResult::Failed`] with
254    /// [`super::ExecutorError::PrepareRollbackFailed`].
255    #[inline]
256    pub fn rollback_prepare<Rn, E>(mut self, rollback_prepare_action: Rn) -> Self
257    where
258        Rn: Runnable<E> + Send + 'static,
259        E: Display,
260    {
261        let mut action = rollback_prepare_action;
262        self.rollback_prepare_action = Some(ArcRunnable::new(move || {
263            action
264                .run()
265                .map_err(|error| CallbackError::with_callback_type("prepare_rollback", error))
266        }));
267        self
268    }
269
270    /// Sets the commit action for a successfully completed prepare action.
271    ///
272    /// Errors returned by this action are converted to [`String`] and replace
273    /// an otherwise successful execution result with a prepare-commit failure.
274    ///
275    /// # Parameters
276    ///
277    /// * `commit_prepare_action` - The action to run if the task succeeds after
278    ///   prepare succeeds.
279    ///
280    /// # Returns
281    ///
282    /// This builder with prepare commit configured.
283    ///
284    /// # Errors
285    ///
286    /// This builder method does not return errors. If `commit_prepare_action`
287    /// later returns an error during execution, the execution result becomes
288    /// [`super::ExecutionResult::Failed`] with
289    /// [`super::ExecutorError::PrepareCommitFailed`].
290    #[inline]
291    pub fn commit_prepare<Rn, E>(mut self, commit_prepare_action: Rn) -> Self
292    where
293        Rn: Runnable<E> + Send + 'static,
294        E: Display,
295    {
296        let mut action = commit_prepare_action;
297        self.commit_prepare_action = Some(ArcRunnable::new(move || {
298            action
299                .run()
300                .map_err(|error| CallbackError::with_callback_type("prepare_commit", error))
301        }));
302        self
303    }
304
305    /// Enables panic capture for tester, prepare callbacks, and task execution.
306    ///
307    /// When enabled, panic payloads are converted to
308    /// [`super::executor_error::ExecutorError::Panic`] and surfaced through
309    /// [`super::ExecutionResult`].
310    ///
311    /// # Returns
312    ///
313    /// This builder with panic capture enabled.
314    #[inline]
315    #[must_use = "assign or chain the returned builder"]
316    pub fn catch_panics(mut self) -> Self {
317        self.catch_panics = true;
318        self
319    }
320
321    /// Derives a builder with panic capture enabled or disabled for tester,
322    /// prepare callbacks, and task execution.
323    ///
324    /// # Parameters
325    ///
326    /// * `catch_panics` - `true` to capture panics as execution errors, or
327    ///   `false` to let panics unwind.
328    ///
329    /// # Returns
330    ///
331    /// A reconfigured builder with the updated panic-capture setting.
332    #[inline]
333    #[must_use = "assign or chain the returned builder"]
334    pub fn with_panic_capture(mut self, catch_panics: bool) -> Self {
335        self.catch_panics = catch_panics;
336        self
337    }
338
339    /// Disables panic capture for tester, prepare callbacks, and task execution.
340    ///
341    /// # Returns
342    ///
343    /// This builder with panic capture disabled.
344    #[inline]
345    #[must_use = "assign or chain the returned builder"]
346    pub fn disable_catch_panics(mut self) -> Self {
347        self.catch_panics = false;
348        self
349    }
350
351    /// Builds the reusable executor.
352    ///
353    /// # Returns
354    ///
355    /// A [`DoubleCheckedLockExecutor`] containing the configured lock, tester,
356    /// execution logger, and prepare lifecycle callbacks.
357    #[inline]
358    pub fn build(self) -> DoubleCheckedLockExecutor<L, T> {
359        DoubleCheckedLockExecutor::new(self)
360    }
361}