qubit_progress/model/progress_event_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 super::{
13 ProgressCounters,
14 ProgressEvent,
15 ProgressPhase,
16 ProgressStage,
17};
18
19/// Builder for [`ProgressEvent`].
20///
21/// The builder keeps the common path compact by letting callers configure
22/// phase, counters, optional stage information, and elapsed time in a single
23/// chain.
24///
25/// # Examples
26///
27/// ```
28/// use std::time::Duration;
29///
30/// use qubit_progress::{
31/// ProgressEvent,
32/// ProgressPhase,
33/// };
34///
35/// let event = ProgressEvent::builder()
36/// .running()
37/// .total(8)
38/// .completed(3)
39/// .active(1)
40/// .stage_named("copy", "Copy files")
41/// .elapsed(Duration::from_secs(2))
42/// .build();
43///
44/// assert_eq!(event.phase(), ProgressPhase::Running);
45/// assert_eq!(event.counters().completed_count(), 3);
46/// assert_eq!(event.stage().map(|stage| stage.id()), Some("copy"));
47/// ```
48#[derive(Debug, Clone, PartialEq)]
49pub struct ProgressEventBuilder {
50 /// Lifecycle phase of the event being built.
51 pub(crate) phase: ProgressPhase,
52 /// Generic counters for the event being built.
53 pub(crate) counters: ProgressCounters,
54 /// Optional current stage.
55 pub(crate) stage: Option<ProgressStage>,
56 /// Monotonic elapsed duration.
57 pub(crate) elapsed: Duration,
58}
59
60impl ProgressEventBuilder {
61 /// Creates a builder with default running progress state.
62 ///
63 /// # Returns
64 ///
65 /// A builder whose phase is [`ProgressPhase::Running`], elapsed duration is
66 /// zero, total count is unknown, and all counters are zero.
67 #[inline]
68 pub const fn new() -> Self {
69 Self {
70 phase: ProgressPhase::Running,
71 counters: ProgressCounters::new(None),
72 stage: None,
73 elapsed: Duration::ZERO,
74 }
75 }
76
77 /// Configures the lifecycle phase.
78 ///
79 /// # Parameters
80 ///
81 /// * `phase` - Lifecycle phase to report.
82 ///
83 /// # Returns
84 ///
85 /// This builder with `phase` recorded.
86 #[inline]
87 pub const fn phase(mut self, phase: ProgressPhase) -> Self {
88 self.phase = phase;
89 self
90 }
91
92 /// Configures the event as started.
93 ///
94 /// # Returns
95 ///
96 /// This builder with [`ProgressPhase::Started`].
97 #[inline]
98 pub const fn started(self) -> Self {
99 self.phase(ProgressPhase::Started)
100 }
101
102 /// Configures the event as running.
103 ///
104 /// # Returns
105 ///
106 /// This builder with [`ProgressPhase::Running`].
107 #[inline]
108 pub const fn running(self) -> Self {
109 self.phase(ProgressPhase::Running)
110 }
111
112 /// Configures the event as finished.
113 ///
114 /// # Returns
115 ///
116 /// This builder with [`ProgressPhase::Finished`].
117 #[inline]
118 pub const fn finished(self) -> Self {
119 self.phase(ProgressPhase::Finished)
120 }
121
122 /// Configures the event as failed.
123 ///
124 /// # Returns
125 ///
126 /// This builder with [`ProgressPhase::Failed`].
127 #[inline]
128 pub const fn failed(self) -> Self {
129 self.phase(ProgressPhase::Failed)
130 }
131
132 /// Configures the event as canceled.
133 ///
134 /// # Returns
135 ///
136 /// This builder with [`ProgressPhase::Canceled`].
137 #[inline]
138 pub const fn canceled(self) -> Self {
139 self.phase(ProgressPhase::Canceled)
140 }
141
142 /// Replaces the current counter set.
143 ///
144 /// # Parameters
145 ///
146 /// * `counters` - Complete counter set to carry in the built event.
147 ///
148 /// # Returns
149 ///
150 /// This builder with `counters` recorded.
151 #[inline]
152 pub const fn counters(mut self, counters: ProgressCounters) -> Self {
153 self.counters = counters;
154 self
155 }
156
157 /// Configures a known total work-unit count.
158 ///
159 /// # Parameters
160 ///
161 /// * `total_count` - Total number of work units.
162 ///
163 /// # Returns
164 ///
165 /// This builder with a known total count.
166 #[inline]
167 pub const fn total(mut self, total_count: usize) -> Self {
168 self.counters = self.counters.with_total_count(Some(total_count));
169 self
170 }
171
172 /// Configures the event as unknown-total progress.
173 ///
174 /// # Returns
175 ///
176 /// This builder with no total count.
177 #[inline]
178 pub const fn unknown_total(mut self) -> Self {
179 self.counters = self.counters.with_total_count(None);
180 self
181 }
182
183 /// Configures the completed work-unit count.
184 ///
185 /// # Parameters
186 ///
187 /// * `completed_count` - Number of completed work units.
188 ///
189 /// # Returns
190 ///
191 /// This builder with `completed_count` recorded.
192 #[inline]
193 pub const fn completed(mut self, completed_count: usize) -> Self {
194 self.counters = self.counters.with_completed_count(completed_count);
195 self
196 }
197
198 /// Configures the active work-unit count.
199 ///
200 /// # Parameters
201 ///
202 /// * `active_count` - Number of currently active work units.
203 ///
204 /// # Returns
205 ///
206 /// This builder with `active_count` recorded.
207 #[inline]
208 pub const fn active(mut self, active_count: usize) -> Self {
209 self.counters = self.counters.with_active_count(active_count);
210 self
211 }
212
213 /// Configures the successful work-unit count.
214 ///
215 /// # Parameters
216 ///
217 /// * `succeeded_count` - Number of successful work units.
218 ///
219 /// # Returns
220 ///
221 /// This builder with `succeeded_count` recorded.
222 #[inline]
223 pub const fn succeeded(mut self, succeeded_count: usize) -> Self {
224 self.counters = self.counters.with_succeeded_count(succeeded_count);
225 self
226 }
227
228 /// Configures the failed work-unit count.
229 ///
230 /// # Parameters
231 ///
232 /// * `failed_count` - Number of failed work units.
233 ///
234 /// # Returns
235 ///
236 /// This builder with `failed_count` recorded.
237 #[inline]
238 pub const fn failed_count(mut self, failed_count: usize) -> Self {
239 self.counters = self.counters.with_failed_count(failed_count);
240 self
241 }
242
243 /// Configures the current stage.
244 ///
245 /// # Parameters
246 ///
247 /// * `stage` - Stage metadata to carry in the built event.
248 ///
249 /// # Returns
250 ///
251 /// This builder with `stage` recorded.
252 #[inline]
253 pub fn stage(mut self, stage: ProgressStage) -> Self {
254 self.stage = Some(stage);
255 self
256 }
257
258 /// Configures the current stage from an id and display name.
259 ///
260 /// # Parameters
261 ///
262 /// * `id` - Stable machine-readable stage identifier.
263 /// * `name` - Human-readable stage name.
264 ///
265 /// # Returns
266 ///
267 /// This builder with a stage created from `id` and `name`.
268 #[inline]
269 pub fn stage_named(self, id: &str, name: &str) -> Self {
270 self.stage(ProgressStage::new(id, name))
271 }
272
273 /// Configures the elapsed duration.
274 ///
275 /// # Parameters
276 ///
277 /// * `elapsed` - Monotonic elapsed duration to carry in the event.
278 ///
279 /// # Returns
280 ///
281 /// This builder with `elapsed` recorded.
282 #[inline]
283 pub const fn elapsed(mut self, elapsed: Duration) -> Self {
284 self.elapsed = elapsed;
285 self
286 }
287
288 /// Builds the progress event.
289 ///
290 /// # Returns
291 ///
292 /// An immutable [`ProgressEvent`] with the configured values.
293 #[inline]
294 pub fn build(self) -> ProgressEvent {
295 ProgressEvent::new(self)
296 }
297}
298
299impl Default for ProgressEventBuilder {
300 /// Creates a builder with default running progress state.
301 ///
302 /// # Returns
303 ///
304 /// A builder equivalent to [`Self::new`].
305 #[inline]
306 fn default() -> Self {
307 Self::new()
308 }
309}