Skip to main content

how_far/
report.rs

1//! Counting completed work.
2
3use crate::{Stop, StopReason};
4use alloc::{boxed::Box, sync::Arc};
5
6/// A sink for **completed** units of work.
7///
8/// `advance` only counts. It never blocks, polls, or reads a clock, so it is
9/// safe in hot loops and on any thread; a phase shared by workers receives
10/// reports from all of them. Report the actual number of finished units,
11/// including a short final batch.
12pub trait Report: Send + Sync {
13    /// Add `completed` finished units.
14    #[track_caller]
15    fn advance(&self, completed: u64);
16
17    /// Whether reports have any effect.
18    ///
19    /// Only sinks that permanently discard reports return `false`. A caller
20    /// may skip work that exists only to compute a count.
21    fn may_report(&self) -> bool {
22        true
23    }
24}
25
26/// Discards every report. It is zero-sized and compiles away in generic code.
27#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
28pub struct NoReport;
29
30impl Report for NoReport {
31    #[inline(always)]
32    fn advance(&self, _: u64) {}
33    #[inline(always)]
34    fn may_report(&self) -> bool {
35        false
36    }
37}
38
39/// Forward `Report` through a pointer-like wrapper.
40macro_rules! forward_report {
41    ($($wrapper:ty),*) => {$(
42        impl<T: Report + ?Sized> Report for $wrapper {
43            #[inline]
44            #[track_caller]
45            fn advance(&self, completed: u64) {
46                (**self).advance(completed);
47            }
48            #[inline]
49            fn may_report(&self) -> bool {
50                (**self).may_report()
51            }
52        }
53    )*};
54}
55forward_report!(&T, &mut T, Box<T>, Arc<T>);
56
57/// `None` discards reports; `Some` forwards them.
58impl<T: Report> Report for Option<T> {
59    #[inline]
60    #[track_caller]
61    fn advance(&self, completed: u64) {
62        if let Some(report) = self {
63            report.advance(completed);
64        }
65    }
66    #[inline]
67    fn may_report(&self) -> bool {
68        self.as_ref().is_some_and(Report::may_report)
69    }
70}
71
72/// A stop policy and a progress sink used as one value.
73///
74/// `check` goes to `stop` and `advance` goes to `report`, so the pair works
75/// wherever `Stop + Report` is expected, including
76/// [`ProgressExt::step`](crate::ProgressExt::step). `ProgressWithStop::new(Unstoppable, NoReport)`
77/// is zero-sized.
78#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
79pub struct ProgressWithStop<S, R> {
80    /// The cancellation policy.
81    pub stop: S,
82    /// The completed-work sink.
83    pub report: R,
84}
85
86impl<S, R> ProgressWithStop<S, R> {
87    /// Pair a stop policy with a progress sink.
88    pub const fn new(stop: S, report: R) -> Self {
89        Self { stop, report }
90    }
91}
92
93impl<S: Stop, R: Send + Sync> Stop for ProgressWithStop<S, R> {
94    #[inline]
95    #[track_caller]
96    fn check(&self) -> Result<(), StopReason> {
97        self.stop.check()
98    }
99    #[inline]
100    fn may_stop(&self) -> bool {
101        self.stop.may_stop()
102    }
103}
104
105impl<S: Send + Sync, R: Report> Report for ProgressWithStop<S, R> {
106    #[inline]
107    #[track_caller]
108    fn advance(&self, completed: u64) {
109        self.report.advance(completed);
110    }
111    #[inline]
112    fn may_report(&self) -> bool {
113        self.report.may_report()
114    }
115}