Skip to main content

qubit_batch/execute/impls/
parallel_batch_executor_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 ******************************************************************************/
10use std::{
11    sync::Arc,
12    time::Duration,
13};
14
15use qubit_progress::reporter::{
16    NoOpProgressReporter,
17    ProgressReporter,
18};
19
20use super::{
21    ParallelBatchExecutor,
22    ParallelBatchExecutorBuildError,
23};
24
25/// Builder for [`ParallelBatchExecutor`].
26///
27/// Use the builder when the default worker count, sequential fallback
28/// threshold, progress interval, or reporter should be customized.
29///
30/// ```rust
31/// use qubit_batch::ParallelBatchExecutor;
32///
33/// let executor = ParallelBatchExecutor::builder()
34///     .thread_count(2)
35///     .sequential_threshold(0)
36///     .build()
37///     .expect("parallel executor configuration should be valid");
38///
39/// assert_eq!(executor.thread_count(), 2);
40/// assert_eq!(executor.sequential_threshold(), 0);
41/// ```
42pub struct ParallelBatchExecutorBuilder {
43    /// Number of worker threads used for parallel executions.
44    thread_count: usize,
45    /// Maximum batch size that still uses sequential execution.
46    sequential_threshold: usize,
47    /// Minimum interval between progress callbacks.
48    report_interval: Duration,
49    /// Reporter receiving batch lifecycle callbacks.
50    reporter: Arc<dyn ProgressReporter>,
51}
52
53impl ParallelBatchExecutorBuilder {
54    /// Sets the worker-thread count.
55    ///
56    /// # Parameters
57    ///
58    /// * `thread_count` - Number of scoped worker threads to use.
59    ///
60    /// # Returns
61    ///
62    /// This builder for fluent configuration.
63    #[inline]
64    pub const fn thread_count(mut self, thread_count: usize) -> Self {
65        self.thread_count = thread_count;
66        self
67    }
68
69    /// Sets the sequential fallback threshold.
70    ///
71    /// # Parameters
72    ///
73    /// * `sequential_threshold` - Maximum batch size that still runs
74    ///   sequentially.
75    ///
76    /// # Returns
77    ///
78    /// This builder for fluent configuration.
79    #[inline]
80    pub const fn sequential_threshold(mut self, sequential_threshold: usize) -> Self {
81        self.sequential_threshold = sequential_threshold;
82        self
83    }
84
85    /// Sets the progress-report interval.
86    ///
87    /// # Parameters
88    ///
89    /// * `report_interval` - Minimum interval between due-based running
90    ///   progress events. Use [`Duration::ZERO`] to report at every
91    ///   implementation-defined running progress point.
92    ///
93    /// # Returns
94    ///
95    /// This builder for fluent configuration.
96    #[inline]
97    pub const fn report_interval(mut self, report_interval: Duration) -> Self {
98        self.report_interval = report_interval;
99        self
100    }
101
102    /// Sets the progress reporter used by built executors.
103    ///
104    /// # Parameters
105    ///
106    /// * `reporter` - Reporter receiving batch lifecycle callbacks.
107    ///
108    /// # Returns
109    ///
110    /// This builder for fluent configuration.
111    #[inline]
112    pub fn reporter<R>(mut self, reporter: R) -> Self
113    where
114        R: ProgressReporter + 'static,
115    {
116        self.reporter = Arc::new(reporter);
117        self
118    }
119
120    /// Sets the shared progress reporter used by built executors.
121    ///
122    /// # Parameters
123    ///
124    /// * `reporter` - Shared reporter receiving batch lifecycle callbacks.
125    ///
126    /// # Returns
127    ///
128    /// This builder for fluent configuration.
129    #[inline]
130    pub fn reporter_arc(mut self, reporter: Arc<dyn ProgressReporter>) -> Self {
131        self.reporter = reporter;
132        self
133    }
134
135    /// Disables progress callbacks by using [`NoOpProgressReporter`].
136    ///
137    /// # Returns
138    ///
139    /// This builder for fluent configuration.
140    #[inline]
141    pub fn no_reporter(mut self) -> Self {
142        self.reporter = Arc::new(NoOpProgressReporter);
143        self
144    }
145
146    /// Builds a validated [`ParallelBatchExecutor`].
147    ///
148    /// # Returns
149    ///
150    /// A parallel batch executor when the configuration is valid.
151    ///
152    /// # Errors
153    ///
154    /// Returns [`ParallelBatchExecutorBuildError`] when the worker count is
155    /// zero.
156    pub fn build(self) -> Result<ParallelBatchExecutor, ParallelBatchExecutorBuildError> {
157        if self.thread_count == 0 {
158            return Err(ParallelBatchExecutorBuildError::ZeroThreadCount);
159        }
160        Ok(ParallelBatchExecutor {
161            thread_count: self.thread_count,
162            sequential_threshold: self.sequential_threshold,
163            report_interval: self.report_interval,
164            reporter: self.reporter,
165        })
166    }
167}
168
169impl Default for ParallelBatchExecutorBuilder {
170    /// Creates a builder with default parallel batch settings.
171    ///
172    /// # Returns
173    ///
174    /// A builder using available parallelism, five-second progress intervals,
175    /// sequential fallback for batches at or below [`ParallelBatchExecutor::DEFAULT_SEQUENTIAL_THRESHOLD`],
176    /// and no-op reporting.
177    fn default() -> Self {
178        Self {
179            thread_count: ParallelBatchExecutor::default_thread_count(),
180            sequential_threshold: ParallelBatchExecutor::DEFAULT_SEQUENTIAL_THRESHOLD,
181            report_interval: ParallelBatchExecutor::DEFAULT_REPORT_INTERVAL,
182            reporter: Arc::new(NoOpProgressReporter),
183        }
184    }
185}