qubit_progress/model/progress_counters.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 ******************************************************************************/
10/// Generic progress counters for a running operation.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct ProgressCounters {
13 /// Total work-unit count when known.
14 total_count: Option<usize>,
15 /// Completed work-unit count.
16 completed_count: usize,
17 /// Active work-unit count.
18 active_count: usize,
19 /// Successful work-unit count.
20 succeeded_count: usize,
21 /// Failed work-unit count.
22 failed_count: usize,
23}
24
25impl ProgressCounters {
26 /// Creates counters with a known or unknown total count.
27 ///
28 /// # Parameters
29 ///
30 /// * `total_count` - Total work-unit count, or `None` when unknown.
31 ///
32 /// # Returns
33 ///
34 /// Zeroed counters with the supplied total count.
35 #[inline]
36 pub const fn new(total_count: Option<usize>) -> Self {
37 Self {
38 total_count,
39 completed_count: 0,
40 active_count: 0,
41 succeeded_count: 0,
42 failed_count: 0,
43 }
44 }
45
46 /// Returns a copy configured with the known or unknown total count.
47 ///
48 /// # Parameters
49 ///
50 /// * `total_count` - Total work-unit count, or `None` when unknown.
51 ///
52 /// # Returns
53 ///
54 /// This counter set with `total_count` recorded.
55 #[inline]
56 pub const fn with_total_count(mut self, total_count: Option<usize>) -> Self {
57 self.total_count = total_count;
58 self
59 }
60
61 /// Returns a copy configured with the completed count.
62 ///
63 /// # Parameters
64 ///
65 /// * `completed_count` - Number of completed work units.
66 ///
67 /// # Returns
68 ///
69 /// This counter set with `completed_count` recorded.
70 #[inline]
71 pub const fn with_completed_count(mut self, completed_count: usize) -> Self {
72 self.completed_count = completed_count;
73 self
74 }
75
76 /// Returns a copy configured with the active count.
77 ///
78 /// # Parameters
79 ///
80 /// * `active_count` - Number of currently active work units.
81 ///
82 /// # Returns
83 ///
84 /// This counter set with `active_count` recorded.
85 #[inline]
86 pub const fn with_active_count(mut self, active_count: usize) -> Self {
87 self.active_count = active_count;
88 self
89 }
90
91 /// Returns a copy configured with the succeeded count.
92 ///
93 /// # Parameters
94 ///
95 /// * `succeeded_count` - Number of successful work units.
96 ///
97 /// # Returns
98 ///
99 /// This counter set with `succeeded_count` recorded.
100 #[inline]
101 pub const fn with_succeeded_count(mut self, succeeded_count: usize) -> Self {
102 self.succeeded_count = succeeded_count;
103 self
104 }
105
106 /// Returns a copy configured with the failed count.
107 ///
108 /// # Parameters
109 ///
110 /// * `failed_count` - Number of failed work units.
111 ///
112 /// # Returns
113 ///
114 /// This counter set with `failed_count` recorded.
115 #[inline]
116 pub const fn with_failed_count(mut self, failed_count: usize) -> Self {
117 self.failed_count = failed_count;
118 self
119 }
120
121 /// Returns the total work-unit count when known.
122 ///
123 /// # Returns
124 ///
125 /// `Some(total)` for known-total progress, or `None` for open-ended
126 /// progress.
127 #[inline]
128 pub const fn total_count(&self) -> Option<usize> {
129 self.total_count
130 }
131
132 /// Returns the completed work-unit count.
133 ///
134 /// # Returns
135 ///
136 /// The number of completed work units.
137 #[inline]
138 pub const fn completed_count(&self) -> usize {
139 self.completed_count
140 }
141
142 /// Returns the active work-unit count.
143 ///
144 /// # Returns
145 ///
146 /// The number of currently active work units.
147 #[inline]
148 pub const fn active_count(&self) -> usize {
149 self.active_count
150 }
151
152 /// Returns the successful work-unit count.
153 ///
154 /// # Returns
155 ///
156 /// The number of successful work units.
157 #[inline]
158 pub const fn succeeded_count(&self) -> usize {
159 self.succeeded_count
160 }
161
162 /// Returns the failed work-unit count.
163 ///
164 /// # Returns
165 ///
166 /// The number of failed work units.
167 #[inline]
168 pub const fn failed_count(&self) -> usize {
169 self.failed_count
170 }
171
172 /// Returns the remaining work-unit count when the total is known.
173 ///
174 /// # Returns
175 ///
176 /// `Some(total - completed - active)` using saturating arithmetic for
177 /// known-total progress, or `None` when the total is unknown.
178 #[inline]
179 pub const fn remaining_count(&self) -> Option<usize> {
180 match self.total_count {
181 Some(total_count) => {
182 Some(total_count.saturating_sub(self.completed_count + self.active_count))
183 }
184 None => None,
185 }
186 }
187
188 /// Returns completed progress as a fraction in `0.0..=1.0`.
189 ///
190 /// # Returns
191 ///
192 /// `Some(fraction)` for known-total progress, `Some(1.0)` when the known
193 /// total is zero, or `None` when the total is unknown.
194 #[inline]
195 pub fn progress_fraction(&self) -> Option<f64> {
196 self.total_count.map(|total_count| {
197 if total_count == 0 {
198 1.0
199 } else {
200 (self.completed_count as f64 / total_count as f64).clamp(0.0, 1.0)
201 }
202 })
203 }
204
205 /// Returns completed progress as a percentage in `0.0..=100.0`.
206 ///
207 /// # Returns
208 ///
209 /// `Some(percent)` for known-total progress, or `None` when the total is
210 /// unknown.
211 #[inline]
212 pub fn progress_percent(&self) -> Option<f64> {
213 self.progress_fraction().map(|fraction| fraction * 100.0)
214 }
215}