1use core::fmt;
2use std::sync::Arc;
3
4use parking_lot::RwLock;
5use zng_state_map::{OwnedStateMap, StateMapMut, StateMapRef};
6use zng_txt::Txt;
7use zng_unit::{Factor, FactorPercent, FactorUnits as _};
8use zng_var::{IntoVar, Var, const_var, impl_from_and_into_var};
9
10#[derive(Clone)]
12pub struct Progress {
13 factor: Factor,
14 msg: Var<Txt>,
15 meta: Arc<RwLock<OwnedStateMap<Progress>>>,
16}
17impl Progress {
18 pub fn indeterminate() -> Self {
20 Self::new(-1.fct())
21 }
22
23 pub fn complete() -> Self {
25 Self::new(1.fct())
26 }
27
28 pub fn from_fct(factor: impl Into<Factor>) -> Self {
33 Self::new(factor.into())
34 }
35
36 pub fn from_n_of(n: u64, total: u64) -> Self {
38 Self::new(Self::normalize_n_of(n, total))
39 }
40
41 pub fn with_msg(mut self, msg: impl IntoVar<Txt>) -> Self {
43 self.msg = msg.into_var();
44 self
45 }
46
47 pub fn with_meta_mut(self, meta: impl FnOnce(StateMapMut<Progress>)) -> Self {
51 meta(self.meta.write().borrow_mut());
52 self
53 }
54
55 pub fn and_fct(mut self, factor: impl Into<Factor>) -> Self {
59 if self.is_indeterminate() {
60 return self;
61 }
62 let factor = Self::normalize_factor(factor.into());
63 if factor < 0.fct() {
64 self.factor = -1.fct();
66 } else {
67 self.factor = (self.factor + factor) / 2.fct();
68 }
69 self
70 }
71
72 pub fn and_n_of(self, n: u64, total: u64) -> Self {
76 self.and_fct(Self::normalize_n_of(n, total))
77 }
78
79 pub fn with_fct(mut self, factor: impl Into<Factor>) -> Self {
83 self.factor = Self::normalize_factor(factor.into());
84 self
85 }
86
87 pub fn with_n_of(mut self, n: u64, total: u64) -> Self {
91 self.factor = Self::normalize_n_of(n, total);
92 self
93 }
94
95 pub fn fct(&self) -> Factor {
99 self.factor
100 }
101
102 pub fn is_indeterminate(&self) -> bool {
104 self.factor < 0.fct()
105 }
106
107 pub fn is_complete(&self) -> bool {
109 self.fct() >= 1.fct()
110 }
111
112 pub fn msg(&self) -> Var<Txt> {
114 self.msg.clone()
115 }
116
117 pub fn with_meta<T>(&self, visitor: impl FnOnce(StateMapRef<Progress>) -> T) -> T {
119 visitor(self.meta.read().borrow())
120 }
121
122 fn normalize_factor(mut value: Factor) -> Factor {
123 if value.0 < 0.0 {
124 if value.0 > -0.001 {
125 value.0 = 0.0;
126 } else {
127 value.0 = -1.0;
129 }
130 } else if value.0 > 1.0 {
131 if value.0 < 1.001 {
132 value.0 = 1.0;
133 } else {
134 value.0 = -1.0;
135 }
136 } else if !value.0.is_finite() {
137 value.0 = -1.0;
138 }
139 value
140 }
141
142 fn normalize_n_of(n: u64, total: u64) -> Factor {
143 if n > total {
144 -1.fct() } else if total == 0 {
146 1.fct() } else {
148 Self::normalize_factor(Factor((n as f64 / total as f64) as f32))
149 }
150 }
151
152 fn new(value: Factor) -> Self {
153 Self {
154 factor: Self::normalize_factor(value),
155 msg: const_var(Txt::from_static("")),
156 meta: Arc::new(RwLock::new(OwnedStateMap::new())),
157 }
158 }
159}
160impl fmt::Debug for Progress {
161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 f.debug_struct("TaskStatus")
163 .field("factor", &self.factor)
164 .field("msg", &self.msg.get())
165 .finish_non_exhaustive()
166 }
167}
168impl fmt::Display for Progress {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 let msg = self.msg.get();
171 if !msg.is_empty() {
172 write!(f, "{msg}")?;
173 if !self.is_indeterminate() {
174 write!(f, " ({})", self.factor.pct())
175 } else {
176 Ok(())
177 }
178 } else if !self.is_indeterminate() {
179 write!(f, "{}", self.factor.pct())
180 } else {
181 Ok(())
182 }
183 }
184}
185impl PartialEq for Progress {
186 fn eq(&self, other: &Self) -> bool {
187 self.factor == other.factor
188 && if self.msg.capabilities().is_const() && other.msg.capabilities().is_const() {
189 self.msg.with(|a| other.msg.with(|b| a == b))
190 } else {
191 self.msg.var_eq(&other.msg)
192 }
193 && {
194 let a = self.meta.read();
195 let b = other.meta.read();
196 let a = a.borrow();
197 let b = b.borrow();
198 a.is_empty() == b.is_empty() && (a.is_empty() || Arc::ptr_eq(&self.meta, &other.meta))
199 }
200 }
201}
202impl Eq for Progress {}
203impl_from_and_into_var! {
204 fn from(completed: Factor) -> Progress {
205 Progress::from_fct(completed)
206 }
207 fn from(completed: FactorPercent) -> Progress {
208 Progress::from_fct(completed)
209 }
210 fn from(completed: f32) -> Progress {
211 Progress::from_fct(completed)
212 }
213 fn from(status: Progress) -> Factor {
214 status.fct()
215 }
216 fn from(status: Progress) -> FactorPercent {
217 status.fct().pct()
218 }
219 fn from(status: Progress) -> f32 {
220 status.fct().0
221 }
222 fn from(n_total: (u64, u64)) -> Progress {
223 Progress::from_n_of(n_total.0, n_total.1)
224 }
225 fn from(indeterminate_message: Txt) -> Progress {
226 Progress::indeterminate().with_msg(indeterminate_message)
227 }
228 fn from(indeterminate_message: &'static str) -> Progress {
229 Progress::indeterminate().with_msg(indeterminate_message)
230 }
231 fn from(indeterminate_or_completed: bool) -> Progress {
232 match indeterminate_or_completed {
233 false => Progress::indeterminate(),
234 true => Progress::from_fct(true),
235 }
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242
243 #[test]
244 fn fct_n1() {
245 let p = Progress::from_fct(-1.fct());
246 assert_eq!(p, Progress::indeterminate());
247 }
248
249 #[test]
250 fn fct_2() {
251 let p = Progress::from_fct(2.fct());
252 assert_eq!(p, Progress::indeterminate());
253 }
254
255 #[test]
256 fn fct_05() {
257 let p = Progress::from_fct(0.5.fct());
258 assert_eq!(p, Progress::from(0.5.fct()));
259 }
260
261 #[test]
262 fn fct_0() {
263 let p = Progress::from_fct(0.fct());
264 assert_eq!(p, Progress::from(0.fct()));
265 }
266
267 #[test]
268 fn fct_1() {
269 let p = Progress::from_fct(1.fct());
270 assert_eq!(p, Progress::from(1.fct()));
271 }
272
273 #[test]
274 fn zero_of_zero() {
275 let p = Progress::from_n_of(0, 0);
276 assert_eq!(p, Progress::complete());
277 }
278
279 #[test]
280 fn ten_of_ten() {
281 let p = Progress::from_n_of(10, 10);
282 assert_eq!(p, Progress::complete());
283 }
284
285 #[test]
286 fn ten_of_one() {
287 let p = Progress::from_n_of(10, 1);
288 assert_eq!(p, Progress::indeterminate());
289 }
290
291 #[test]
292 fn five_of_ten() {
293 let p = Progress::from_n_of(5, 10);
294 assert_eq!(p, Progress::from(50.pct()));
295 }
296}