qubit_progress/model/progress_stage.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 serde::{
11 Deserialize,
12 Serialize,
13};
14
15/// Describes the current stage of a multi-stage operation.
16///
17/// # Examples
18///
19/// ```
20/// use qubit_progress::ProgressStage;
21///
22/// let stage = ProgressStage::new("verify", "Verify files")
23/// .with_index(2)
24/// .with_total_stages(4)
25/// .with_weight(0.25);
26///
27/// assert_eq!(stage.id(), "verify");
28/// assert_eq!(stage.name(), "Verify files");
29/// assert_eq!(stage.index(), Some(2));
30/// ```
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32pub struct ProgressStage {
33 /// Stable machine-readable stage identifier.
34 id: String,
35 /// Human-readable stage name.
36 name: String,
37 /// Zero-based stage index when known.
38 #[serde(skip_serializing_if = "Option::is_none")]
39 index: Option<usize>,
40 /// Total number of stages when known.
41 #[serde(skip_serializing_if = "Option::is_none")]
42 total_stages: Option<usize>,
43 /// Relative stage weight when the caller uses weighted progress.
44 #[serde(skip_serializing_if = "Option::is_none")]
45 weight: Option<f64>,
46}
47
48impl ProgressStage {
49 /// Creates a stage with a stable id and display name.
50 ///
51 /// # Parameters
52 ///
53 /// * `id` - Stable machine-readable identifier.
54 /// * `name` - Human-readable stage name.
55 ///
56 /// # Returns
57 ///
58 /// A stage with no index, total stage count, or weight.
59 #[inline]
60 pub fn new(id: &str, name: &str) -> Self {
61 Self {
62 id: id.to_owned(),
63 name: name.to_owned(),
64 index: None,
65 total_stages: None,
66 weight: None,
67 }
68 }
69
70 /// Returns a copy configured with a zero-based stage index.
71 ///
72 /// # Parameters
73 ///
74 /// * `index` - Zero-based stage index.
75 ///
76 /// # Returns
77 ///
78 /// This stage with `index` recorded.
79 #[inline]
80 #[must_use]
81 pub const fn with_index(mut self, index: usize) -> Self {
82 self.index = Some(index);
83 self
84 }
85
86 /// Returns a copy configured with the total stage count.
87 ///
88 /// # Parameters
89 ///
90 /// * `total_stages` - Total number of stages in the operation.
91 ///
92 /// # Returns
93 ///
94 /// This stage with `total_stages` recorded.
95 #[inline]
96 #[must_use]
97 pub const fn with_total_stages(mut self, total_stages: usize) -> Self {
98 self.total_stages = Some(total_stages);
99 self
100 }
101
102 /// Returns a copy configured with a relative stage weight.
103 ///
104 /// The weight is intended for caller-side weighted progress calculations.
105 /// Callers should supply finite, non-negative values. This method records
106 /// the supplied value as-is and does not validate `NaN`, infinity, or
107 /// negative input.
108 ///
109 /// # Parameters
110 ///
111 /// * `weight` - Finite, non-negative relative stage weight used by callers
112 /// that compute weighted total progress.
113 ///
114 /// # Returns
115 ///
116 /// This stage with `weight` recorded.
117 #[inline]
118 #[must_use]
119 pub const fn with_weight(mut self, weight: f64) -> Self {
120 self.weight = Some(weight);
121 self
122 }
123
124 /// Returns the stable stage identifier.
125 ///
126 /// # Returns
127 ///
128 /// The machine-readable stage id.
129 #[inline]
130 pub fn id(&self) -> &str {
131 self.id.as_str()
132 }
133
134 /// Returns the human-readable stage name.
135 ///
136 /// # Returns
137 ///
138 /// The display name for this stage.
139 #[inline]
140 pub fn name(&self) -> &str {
141 self.name.as_str()
142 }
143
144 /// Returns the stage index when known.
145 ///
146 /// # Returns
147 ///
148 /// `Some(index)` when a zero-based stage index was supplied, otherwise
149 /// `None`.
150 #[inline]
151 pub const fn index(&self) -> Option<usize> {
152 self.index
153 }
154
155 /// Returns the total stage count when known.
156 ///
157 /// # Returns
158 ///
159 /// `Some(total)` when a total stage count was supplied, otherwise `None`.
160 #[inline]
161 pub const fn total_stages(&self) -> Option<usize> {
162 self.total_stages
163 }
164
165 /// Returns the relative stage weight when known.
166 ///
167 /// # Returns
168 ///
169 /// `Some(weight)` when a weight was supplied, otherwise `None`.
170 #[inline]
171 pub const fn weight(&self) -> Option<f64> {
172 self.weight
173 }
174}