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}