Coverage Report

Created: 2026-02-14 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/prodash-31.0.0/src/tree/item.rs
Line
Count
Source
1
use std::{
2
    fmt::Debug,
3
    ops::Deref,
4
    sync::{
5
        atomic::{AtomicUsize, Ordering},
6
        Arc,
7
    },
8
    time::SystemTime,
9
};
10
11
use parking_lot::Mutex;
12
13
use crate::{
14
    messages::MessageLevel,
15
    progress::{Id, State, Step, StepShared, Task, Value},
16
    tree::Item,
17
    unit::Unit,
18
};
19
20
impl Drop for Item {
21
0
    fn drop(&mut self) {
22
0
        self.tree.remove(&self.key);
23
0
    }
24
}
25
26
impl Debug for Item {
27
0
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28
0
        f.debug_struct("Item")
29
0
            .field("key", &self.key)
30
0
            .field("value", &self.value)
31
0
            .finish_non_exhaustive()
32
0
    }
33
}
34
35
impl Item {
36
    /// Initialize the Item for receiving progress information.
37
    ///
38
    /// If `max` is `Some(…)`, it will be treated as upper bound. When progress is [set(…)](./struct.Item.html#method.set)
39
    /// it should not exceed the given maximum.
40
    /// If `max` is `None`, the progress is unbounded. Use this if the amount of work cannot accurately
41
    /// be determined.
42
    ///
43
    /// If `unit` is `Some(…)`, it is used for display purposes only. It should be using the plural.
44
    ///
45
    /// If this method is never called, this `Item` will serve as organizational unit, useful to add more structure
46
    /// to the progress tree.
47
    ///
48
    /// **Note** that this method can be called multiple times, changing the bounded-ness and unit at will.
49
0
    pub fn init(&self, max: Option<usize>, unit: Option<Unit>) {
50
        #[cfg(feature = "progress-tree-hp-hashmap")]
51
        {
52
            if let Some(mut r) = self.tree.get_mut(&self.key) {
53
                self.value.store(0, Ordering::SeqCst);
54
                r.value_mut().progress = (max.is_some() || unit.is_some()).then(|| Value {
55
                    done_at: max,
56
                    unit,
57
                    step: Arc::clone(&self.value),
58
                    ..Default::default()
59
                })
60
            };
61
        }
62
        #[cfg(not(feature = "progress-tree-hp-hashmap"))]
63
        {
64
0
            self.tree.get_mut(&self.key, |v| {
65
0
                self.value.store(0, Ordering::SeqCst);
66
0
                v.progress = (max.is_some() || unit.is_some()).then(|| Value {
67
0
                    done_at: max,
68
0
                    unit,
69
0
                    step: Arc::clone(&self.value),
70
0
                    ..Default::default()
71
0
                });
72
0
            });
73
        }
74
0
    }
75
76
0
    fn alter_progress(&self, f: impl FnMut(&mut Value)) {
77
        #[cfg(feature = "progress-tree-hp-hashmap")]
78
        {
79
            if let Some(mut r) = self.tree.get_mut(&self.key) {
80
                // NOTE: since we wrap around, if there are more tasks than we can have IDs for,
81
                // and if all these tasks are still alive, two progress trees may see the same ID
82
                // when these go out of scope, they delete the key and the other tree will not find
83
                // its value anymore. Besides, it's probably weird to see tasks changing their progress
84
                // all the time…
85
                r.value_mut().progress.as_mut().map(f);
86
            };
87
        }
88
        #[cfg(not(feature = "progress-tree-hp-hashmap"))]
89
        {
90
0
            self.tree.get_mut(&self.key, |v| {
91
0
                v.progress.as_mut().map(f);
92
0
            });
Unexecuted instantiation: <prodash::tree::Item>::alter_progress::<<prodash::tree::Item>::halted::{closure#0}>::{closure#0}
Unexecuted instantiation: <prodash::tree::Item>::alter_progress::<<prodash::tree::Item>::blocked::{closure#0}>::{closure#0}
Unexecuted instantiation: <prodash::tree::Item>::alter_progress::<<prodash::tree::Item>::running::{closure#0}>::{closure#0}
93
        }
94
0
    }
Unexecuted instantiation: <prodash::tree::Item>::alter_progress::<<prodash::tree::Item>::halted::{closure#0}>
Unexecuted instantiation: <prodash::tree::Item>::alter_progress::<<prodash::tree::Item>::blocked::{closure#0}>
Unexecuted instantiation: <prodash::tree::Item>::alter_progress::<<prodash::tree::Item>::running::{closure#0}>
95
96
    /// Set the name of this task's progress to the given `name`.
97
0
    pub fn set_name(&self, name: impl Into<String>) {
98
        #[cfg(feature = "progress-tree-hp-hashmap")]
99
        {
100
            if let Some(mut r) = self.tree.get_mut(&self.key) {
101
                r.value_mut().name = name.into();
102
            };
103
        }
104
        #[cfg(not(feature = "progress-tree-hp-hashmap"))]
105
        {
106
0
            self.tree.get_mut(&self.key, |v| {
107
0
                v.name = name.into();
108
0
            });
109
        }
110
0
    }
111
112
    /// Get the name of this task's progress
113
0
    pub fn name(&self) -> Option<String> {
114
        #[cfg(feature = "progress-tree-hp-hashmap")]
115
        {
116
            self.tree.get(&self.key).map(|r| r.value().name.to_owned())
117
        }
118
        #[cfg(not(feature = "progress-tree-hp-hashmap"))]
119
        {
120
0
            self.tree.get(&self.key, |v| v.name.to_owned())
121
        }
122
0
    }
123
124
    /// Get the stable identifier of this instance.
125
0
    pub fn id(&self) -> Id {
126
        #[cfg(feature = "progress-tree-hp-hashmap")]
127
        {
128
            self.tree
129
                .get(&self.key)
130
                .map(|r| r.value().id)
131
                .unwrap_or(crate::progress::UNKNOWN)
132
        }
133
        #[cfg(not(feature = "progress-tree-hp-hashmap"))]
134
        {
135
0
            self.tree.get(&self.key, |v| v.id).unwrap_or(crate::progress::UNKNOWN)
136
        }
137
0
    }
138
139
    /// Returns the current step, as controlled by `inc*(…)` calls
140
0
    pub fn step(&self) -> Option<Step> {
141
0
        self.value.load(Ordering::Relaxed).into()
142
0
    }
143
144
    /// Returns the maximum about of items we expect, as provided with the `init(…)` call
145
0
    pub fn max(&self) -> Option<Step> {
146
        #[cfg(feature = "progress-tree-hp-hashmap")]
147
        {
148
            self.tree
149
                .get(&self.key)
150
                .and_then(|r| r.value().progress.as_ref().and_then(|p| p.done_at))
151
        }
152
        #[cfg(not(feature = "progress-tree-hp-hashmap"))]
153
        {
154
0
            self.tree
155
0
                .get(&self.key, |v| v.progress.as_ref().and_then(|p| p.done_at))
156
0
                .flatten()
157
        }
158
0
    }
159
160
    /// Set the maximum value to `max` and return the old maximum value.
161
0
    pub fn set_max(&self, max: Option<Step>) -> Option<Step> {
162
        #[cfg(feature = "progress-tree-hp-hashmap")]
163
        {
164
            self.tree
165
                .get_mut(&self.key)?
166
                .value_mut()
167
                .progress
168
                .as_mut()
169
                .and_then(|p| {
170
                    let prev = p.done_at;
171
                    p.done_at = max;
172
                    prev
173
                })
174
        }
175
        #[cfg(not(feature = "progress-tree-hp-hashmap"))]
176
        {
177
0
            self.tree
178
0
                .get_mut(&self.key, |v| {
179
0
                    v.progress.as_mut().and_then(|p| {
180
0
                        let prev = p.done_at;
181
0
                        p.done_at = max;
182
0
                        prev
183
0
                    })
184
0
                })
185
0
                .flatten()
186
        }
187
0
    }
188
189
    /// Returns the (cloned) unit associated with this Progress
190
0
    pub fn unit(&self) -> Option<Unit> {
191
        #[cfg(feature = "progress-tree-hp-hashmap")]
192
        {
193
            self.tree
194
                .get(&self.key)
195
                .and_then(|r| r.value().progress.as_ref().and_then(|p| p.unit.clone()))
196
        }
197
        #[cfg(not(feature = "progress-tree-hp-hashmap"))]
198
        {
199
0
            self.tree
200
0
                .get(&self.key, |v| v.progress.as_ref().and_then(|p| p.unit.clone()))
201
0
                .flatten()
202
        }
203
0
    }
204
205
    /// Set the current progress to the given `step`.
206
    ///
207
    /// **Note**: that this call has no effect unless `init(…)` was called before.
208
0
    pub fn set(&self, step: Step) {
209
0
        self.value.store(step, Ordering::SeqCst);
210
0
    }
211
212
    /// Increment the current progress by the given `step`.
213
    ///
214
    /// **Note**: that this call has no effect unless `init(…)` was called before.
215
0
    pub fn inc_by(&self, step: Step) {
216
0
        self.value.fetch_add(step, Ordering::Relaxed);
217
0
    }
218
219
    /// Increment the current progress by one.
220
    ///
221
    /// **Note**: that this call has no effect unless `init(…)` was called before.
222
0
    pub fn inc(&self) {
223
0
        self.value.fetch_add(1, Ordering::Relaxed);
224
0
    }
225
226
    /// Call to indicate that progress cannot be indicated, and that the task cannot be interrupted.
227
    /// Use this, as opposed to `halted(…)`, if a non-interruptable call is about to be made without support
228
    /// for any progress indication.
229
    ///
230
    /// If `eta` is `Some(…)`, it specifies the time at which this task is expected to
231
    /// make progress again.
232
    ///
233
    /// The halted-state is undone next time [`tree::Item::running(…)`][Item::running()] is called.
234
0
    pub fn blocked(&self, reason: &'static str, eta: Option<SystemTime>) {
235
0
        self.alter_progress(|p| p.state = State::Blocked(reason, eta));
236
0
    }
237
238
    /// Call to indicate that progress cannot be indicated, even though the task can be interrupted.
239
    /// Use this, as opposed to `blocked(…)`, if an interruptable call is about to be made without support
240
    /// for any progress indication.
241
    ///
242
    /// If `eta` is `Some(…)`, it specifies the time at which this task is expected to
243
    /// make progress again.
244
    ///
245
    /// The halted-state is undone next time [`tree::Item::running(…)`][Item::running()] is called.
246
0
    pub fn halted(&self, reason: &'static str, eta: Option<SystemTime>) {
247
0
        self.alter_progress(|p| p.state = State::Halted(reason, eta));
248
0
    }
249
250
    /// Call to indicate that progress is back in running state, which should be called after the reason for
251
    /// calling `blocked()` or `halted()` has passed.
252
0
    pub fn running(&self) {
253
0
        self.alter_progress(|p| p.state = State::Running);
254
0
    }
255
256
    /// Adds a new child `Tree`, whose parent is this instance, with the given `name`.
257
    ///
258
    /// **Important**: The depth of the hierarchy is limited to [`tree::Key::max_level`](./struct.Key.html#method.max_level).
259
    /// Exceeding the level will be ignored, and new tasks will be added to this instance's
260
    /// level instead.
261
0
    pub fn add_child(&mut self, name: impl Into<String>) -> Item {
262
0
        self.add_child_with_id(name, crate::progress::UNKNOWN)
263
0
    }
264
265
    /// Adds a new child `Tree`, whose parent is this instance, with the given `name` and `id`.
266
    ///
267
    /// **Important**: The depth of the hierarchy is limited to [`tree::Key::max_level`](./struct.Key.html#method.max_level).
268
    /// Exceeding the level will be ignored, and new tasks will be added to this instance's
269
    /// level instead.
270
0
    pub fn add_child_with_id(&mut self, name: impl Into<String>, id: Id) -> Item {
271
0
        let child_key = self.key.add_child(self.highest_child_id);
272
0
        let task = Task {
273
0
            name: name.into(),
274
0
            id,
275
0
            progress: None,
276
0
        };
277
        #[cfg(feature = "progress-tree-hp-hashmap")]
278
        self.tree.insert(child_key, task);
279
        #[cfg(not(feature = "progress-tree-hp-hashmap"))]
280
0
        self.tree.insert(child_key, task);
281
0
        self.highest_child_id = self.highest_child_id.wrapping_add(1);
282
0
        Item {
283
0
            highest_child_id: 0,
284
0
            value: Default::default(),
285
0
            key: child_key,
286
0
            tree: Arc::clone(&self.tree),
287
0
            messages: Arc::clone(&self.messages),
288
0
        }
289
0
    }
290
291
    /// Create a `message` of the given `level` and store it with the progress tree.
292
    ///
293
    /// Use this to provide additional,human-readable information about the progress
294
    /// made, including indicating success or failure.
295
0
    pub fn message(&self, level: MessageLevel, message: impl Into<String>) {
296
0
        let message: String = message.into();
297
0
        self.messages.lock().push_overwrite(
298
0
            level,
299
            {
300
                let name;
301
                #[cfg(feature = "progress-tree-hp-hashmap")]
302
                {
303
                    name = self.tree.get(&self.key).map(|v| v.name.to_owned()).unwrap_or_default();
304
                }
305
                #[cfg(not(feature = "progress-tree-hp-hashmap"))]
306
                {
307
0
                    name = self.tree.get(&self.key, |v| v.name.to_owned()).unwrap_or_default()
308
                }
309
310
                #[cfg(feature = "progress-tree-log")]
311
                match level {
312
                    MessageLevel::Failure => crate::warn!("{} → {}", name, message),
313
                    MessageLevel::Info | MessageLevel::Success => crate::info!("{} → {}", name, message),
314
                };
315
316
0
                name
317
            },
318
0
            message,
319
        )
320
0
    }
321
322
    /// Create a message indicating the task is done
323
0
    pub fn done(&mut self, message: impl Into<String>) {
324
0
        self.message(MessageLevel::Success, message)
325
0
    }
326
327
    /// Create a message indicating the task failed
328
0
    pub fn fail(&mut self, message: impl Into<String>) {
329
0
        self.message(MessageLevel::Failure, message)
330
0
    }
331
332
    /// Create a message providing additional information about the progress thus far.
333
0
    pub fn info(&mut self, message: impl Into<String>) {
334
0
        self.message(MessageLevel::Info, message)
335
0
    }
336
337
0
    pub(crate) fn deep_clone(&self) -> Item {
338
0
        Item {
339
0
            key: self.key,
340
0
            value: Arc::new(AtomicUsize::new(self.value.load(Ordering::SeqCst))),
341
0
            highest_child_id: self.highest_child_id,
342
0
            tree: Arc::new(self.tree.deref().clone()),
343
0
            messages: Arc::new(Mutex::new(self.messages.lock().clone())),
344
0
        }
345
0
    }
346
}
347
348
impl crate::Count for Item {
349
0
    fn set(&self, step: usize) {
350
0
        Item::set(self, step)
351
0
    }
352
353
0
    fn step(&self) -> usize {
354
0
        Item::step(self).unwrap_or(0)
355
0
    }
356
357
0
    fn inc_by(&self, step: usize) {
358
0
        self.inc_by(step)
359
0
    }
360
361
0
    fn counter(&self) -> StepShared {
362
0
        Arc::clone(&self.value)
363
0
    }
364
}
365
366
impl crate::Progress for Item {
367
0
    fn init(&mut self, max: Option<Step>, unit: Option<Unit>) {
368
0
        Item::init(self, max, unit)
369
0
    }
370
371
0
    fn unit(&self) -> Option<Unit> {
372
0
        Item::unit(self)
373
0
    }
374
375
0
    fn max(&self) -> Option<usize> {
376
0
        Item::max(self)
377
0
    }
378
379
0
    fn set_max(&mut self, max: Option<Step>) -> Option<Step> {
380
0
        Item::set_max(self, max)
381
0
    }
382
383
0
    fn set_name(&mut self, name: String) {
384
0
        Item::set_name(self, name)
385
0
    }
386
387
0
    fn name(&self) -> Option<String> {
388
0
        Item::name(self)
389
0
    }
390
391
0
    fn id(&self) -> Id {
392
0
        Item::id(self)
393
0
    }
394
395
0
    fn message(&self, level: MessageLevel, message: String) {
396
0
        Item::message(self, level, message)
397
0
    }
398
}
399
400
impl crate::NestedProgress for Item {
401
    type SubProgress = Item;
402
403
0
    fn add_child(&mut self, name: impl Into<String>) -> Self {
404
0
        Item::add_child(self, name)
405
0
    }
406
407
0
    fn add_child_with_id(&mut self, name: impl Into<String>, id: Id) -> Self {
408
0
        Item::add_child_with_id(self, name, id)
409
0
    }
410
}