Skip to main content

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}