Skip to main content

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}