Skip to main content

qubit_batch/process/
batch_process_result_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::time::Duration;
11
12use crate::{
13    BatchProcessResult,
14    BatchProcessResultBuildError,
15};
16
17/// Builder carrying validated parts for a [`crate::BatchProcessResult`].
18///
19/// The builder checks that completed, processed, and chunk counters describe a
20/// consistent processing result before creating the result.
21///
22/// ```rust
23/// use std::time::Duration;
24///
25/// use qubit_batch::BatchProcessResultBuilder;
26///
27/// let result = BatchProcessResultBuilder::builder(3)
28///     .completed_count(3)
29///     .processed_count(3)
30///     .chunk_count(1)
31///     .elapsed(Duration::ZERO)
32///     .build()
33///     .expect("process result counters should be consistent");
34///
35/// assert!(result.is_success());
36/// assert_eq!(result.chunk_count(), 1);
37/// ```
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct BatchProcessResultBuilder {
40    /// Declared item count for the batch.
41    pub(crate) item_count: usize,
42    /// Number of input items whose processing reached a terminal outcome.
43    pub(crate) completed_count: usize,
44    /// Number of items reported as successfully processed.
45    pub(crate) processed_count: usize,
46    /// Number of chunks submitted by the processor.
47    pub(crate) chunk_count: usize,
48    /// Total monotonic elapsed duration for the batch.
49    pub(crate) elapsed: Duration,
50}
51
52impl BatchProcessResultBuilder {
53    /// Starts building a batch process result.
54    ///
55    /// # Parameters
56    ///
57    /// * `item_count` - Declared item count for the batch.
58    ///
59    /// # Returns
60    ///
61    /// A builder initialized with zero counters and zero elapsed time.
62    #[inline]
63    pub const fn builder(item_count: usize) -> Self {
64        Self {
65            item_count,
66            completed_count: 0,
67            processed_count: 0,
68            chunk_count: 0,
69            elapsed: Duration::ZERO,
70        }
71    }
72
73    /// Sets the number of input items whose processing reached a terminal outcome.
74    ///
75    /// # Parameters
76    ///
77    /// * `completed_count` - Number of completed input items.
78    ///
79    /// # Returns
80    ///
81    /// The updated builder.
82    #[inline]
83    pub const fn completed_count(mut self, completed_count: usize) -> Self {
84        self.completed_count = completed_count;
85        self
86    }
87
88    /// Sets the number of items reported as successfully processed.
89    ///
90    /// # Parameters
91    ///
92    /// * `processed_count` - Number of successfully processed items.
93    ///
94    /// # Returns
95    ///
96    /// The updated builder.
97    #[inline]
98    pub const fn processed_count(mut self, processed_count: usize) -> Self {
99        self.processed_count = processed_count;
100        self
101    }
102
103    /// Sets the number of chunks submitted by the processor.
104    ///
105    /// # Parameters
106    ///
107    /// * `chunk_count` - Number of submitted chunks.
108    ///
109    /// # Returns
110    ///
111    /// The updated builder.
112    #[inline]
113    pub const fn chunk_count(mut self, chunk_count: usize) -> Self {
114        self.chunk_count = chunk_count;
115        self
116    }
117
118    /// Sets the total monotonic elapsed duration.
119    ///
120    /// # Parameters
121    ///
122    /// * `elapsed` - Total monotonic elapsed duration.
123    ///
124    /// # Returns
125    ///
126    /// The updated builder.
127    #[inline]
128    pub const fn elapsed(mut self, elapsed: Duration) -> Self {
129        self.elapsed = elapsed;
130        self
131    }
132
133    /// Validates this builder.
134    ///
135    /// # Returns
136    ///
137    /// `Ok(builder)` when all counters are consistent.
138    ///
139    /// # Errors
140    ///
141    /// Returns [`BatchProcessResultBuildError`] when the counters are
142    /// inconsistent.
143    #[inline]
144    pub fn validate(self) -> Result<Self, BatchProcessResultBuildError> {
145        validate_process_result_invariants(
146            self.item_count,
147            self.completed_count,
148            self.processed_count,
149            self.chunk_count,
150        )?;
151        Ok(self)
152    }
153
154    /// Validates this builder and creates a batch process result.
155    ///
156    /// # Returns
157    ///
158    /// `Ok(result)` when all counters are consistent.
159    ///
160    /// # Errors
161    ///
162    /// Returns [`BatchProcessResultBuildError`] when the counters are
163    /// inconsistent.
164    #[inline]
165    pub fn build(self) -> Result<BatchProcessResult, BatchProcessResultBuildError> {
166        self.validate().map(BatchProcessResult::new)
167    }
168}
169
170/// Validates all counters for a batch process result.
171fn validate_process_result_invariants(
172    item_count: usize,
173    completed_count: usize,
174    processed_count: usize,
175    chunk_count: usize,
176) -> Result<(), BatchProcessResultBuildError> {
177    if completed_count > item_count {
178        return Err(BatchProcessResultBuildError::CompletedCountExceeded {
179            item_count,
180            completed_count,
181        });
182    }
183    if processed_count > completed_count {
184        return Err(BatchProcessResultBuildError::ProcessedCountExceeded {
185            completed_count,
186            processed_count,
187        });
188    }
189    if completed_count > 0 && chunk_count == 0 {
190        return Err(
191            BatchProcessResultBuildError::MissingChunkForCompletedItems { completed_count },
192        );
193    }
194    if chunk_count > completed_count {
195        return Err(BatchProcessResultBuildError::ChunkCountExceeded {
196            completed_count,
197            chunk_count,
198        });
199    }
200    Ok(())
201}