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 ProgressCounter,
14 ProgressEvent,
15 ProgressPhase,
16 ProgressSchema,
17 ProgressStage,
18};
19
20/// Builder for [`ProgressEvent`].
21///
22/// The builder keeps the common path compact by carrying the event schema and
23/// letting callers append named metric counters with a closure.
24///
25/// # Examples
26///
27/// ```
28/// use std::time::Duration;
29///
30/// use qubit_progress::{
31/// ProgressEvent,
32/// ProgressMetric,
33/// ProgressPhase,
34/// ProgressSchema,
35/// };
36///
37/// let schema = ProgressSchema::new(vec![ProgressMetric::new("bytes", "Bytes")]);
38/// let event = ProgressEvent::builder(schema)
39/// .running()
40/// .counter("bytes", |counter| counter.total(8).completed(3).active(1))
41/// .stage_named("copy", "Copy files")
42/// .elapsed(Duration::from_secs(2))
43/// .build();
44///
45/// assert_eq!(event.phase(), ProgressPhase::Running);
46/// assert_eq!(event.counter("bytes").map(|c| c.completed_count()), Some(3));
47/// assert_eq!(event.stage().map(|stage| stage.id()), Some("copy"));
48/// ```
49#[derive(Debug, Clone, PartialEq)]
50pub struct ProgressEventBuilder {
51 /// Metric schema carried by the event being built.
52 pub(crate) schema: ProgressSchema,
53 /// Lifecycle phase of the event being built.
54 pub(crate) phase: ProgressPhase,
55 /// Metric counters for the event being built.
56 pub(crate) counters: Vec<ProgressCounter>,
57 /// Optional current stage.
58 pub(crate) stage: Option<ProgressStage>,
59 /// Monotonic elapsed duration.
60 pub(crate) elapsed: Duration,
61}
62
63impl ProgressEventBuilder {
64 /// Creates a builder for a schema.
65 ///
66 /// # Parameters
67 ///
68 /// * `schema` - Metric schema carried by the built event.
69 ///
70 /// # Returns
71 ///
72 /// A builder whose phase is [`ProgressPhase::Running`], elapsed duration is
73 /// zero, and counters are empty.
74 #[inline]
75 pub fn new(schema: ProgressSchema) -> Self {
76 Self {
77 schema,
78 phase: ProgressPhase::Running,
79 counters: Vec::new(),
80 stage: None,
81 elapsed: Duration::ZERO,
82 }
83 }
84
85 /// Configures the lifecycle phase.
86 ///
87 /// # Parameters
88 ///
89 /// * `phase` - Lifecycle phase to report.
90 ///
91 /// # Returns
92 ///
93 /// This builder with `phase` recorded.
94 #[inline]
95 #[must_use]
96 pub const fn phase(mut self, phase: ProgressPhase) -> Self {
97 self.phase = phase;
98 self
99 }
100
101 /// Configures the event as started.
102 ///
103 /// # Returns
104 ///
105 /// This builder with [`ProgressPhase::Started`].
106 #[inline]
107 #[must_use]
108 pub const fn started(self) -> Self {
109 self.phase(ProgressPhase::Started)
110 }
111
112 /// Configures the event as running.
113 ///
114 /// # Returns
115 ///
116 /// This builder with [`ProgressPhase::Running`].
117 #[inline]
118 #[must_use]
119 pub const fn running(self) -> Self {
120 self.phase(ProgressPhase::Running)
121 }
122
123 /// Configures the event as finished.
124 ///
125 /// # Returns
126 ///
127 /// This builder with [`ProgressPhase::Finished`].
128 #[inline]
129 #[must_use]
130 pub const fn finished(self) -> Self {
131 self.phase(ProgressPhase::Finished)
132 }
133
134 /// Configures the event as failed.
135 ///
136 /// # Returns
137 ///
138 /// This builder with [`ProgressPhase::Failed`].
139 #[inline]
140 #[must_use]
141 pub const fn failed(self) -> Self {
142 self.phase(ProgressPhase::Failed)
143 }
144
145 /// Configures the event as canceled.
146 ///
147 /// # Returns
148 ///
149 /// This builder with [`ProgressPhase::Canceled`].
150 #[inline]
151 #[must_use]
152 pub const fn canceled(self) -> Self {
153 self.phase(ProgressPhase::Canceled)
154 }
155
156 /// Replaces the current counter list.
157 ///
158 /// # Parameters
159 ///
160 /// * `counters` - Complete counter list to carry in the built event.
161 ///
162 /// # Returns
163 ///
164 /// This builder with `counters` recorded.
165 #[inline]
166 #[must_use]
167 pub fn counters(mut self, counters: Vec<ProgressCounter>) -> Self {
168 self.counters = counters;
169 self
170 }
171
172 /// Appends one configured counter.
173 ///
174 /// # Parameters
175 ///
176 /// * `metric_id` - Metric identifier for the counter.
177 /// * `configure` - Closure that fills the counter values.
178 ///
179 /// # Returns
180 ///
181 /// This builder with the configured counter appended.
182 #[inline]
183 #[must_use]
184 pub fn counter<F>(mut self, metric_id: &str, configure: F) -> Self
185 where
186 F: FnOnce(ProgressCounter) -> ProgressCounter,
187 {
188 self.counters.push(configure(ProgressCounter::new(metric_id)));
189 self
190 }
191
192 /// Appends a prebuilt counter.
193 ///
194 /// # Parameters
195 ///
196 /// * `counter` - Counter to append.
197 ///
198 /// # Returns
199 ///
200 /// This builder with `counter` appended.
201 #[inline]
202 #[must_use]
203 pub fn add_counter(mut self, counter: ProgressCounter) -> Self {
204 self.counters.push(counter);
205 self
206 }
207
208 /// Configures the current stage.
209 ///
210 /// # Parameters
211 ///
212 /// * `stage` - Stage metadata to carry in the built event.
213 ///
214 /// # Returns
215 ///
216 /// This builder with `stage` recorded.
217 #[inline]
218 #[must_use]
219 pub fn stage(mut self, stage: ProgressStage) -> Self {
220 self.stage = Some(stage);
221 self
222 }
223
224 /// Configures the current stage from an id and display name.
225 ///
226 /// # Parameters
227 ///
228 /// * `id` - Stable machine-readable stage identifier.
229 /// * `name` - Human-readable stage name.
230 ///
231 /// # Returns
232 ///
233 /// This builder with a stage created from `id` and `name`.
234 #[inline]
235 #[must_use]
236 pub fn stage_named(self, id: &str, name: &str) -> Self {
237 self.stage(ProgressStage::new(id, name))
238 }
239
240 /// Configures the elapsed duration.
241 ///
242 /// # Parameters
243 ///
244 /// * `elapsed` - Monotonic elapsed duration to carry in the event.
245 ///
246 /// # Returns
247 ///
248 /// This builder with `elapsed` recorded.
249 #[inline]
250 #[must_use]
251 pub const fn elapsed(mut self, elapsed: Duration) -> Self {
252 self.elapsed = elapsed;
253 self
254 }
255
256 /// Builds the progress event.
257 ///
258 /// # Returns
259 ///
260 /// An immutable [`ProgressEvent`] with the configured values.
261 #[inline]
262 pub fn build(self) -> ProgressEvent {
263 ProgressEvent::new(self)
264 }
265}