progress_monitor/monitor/
callback.rs

1use std::{
2    borrow::Cow,
3    fmt::{Debug, Display},
4};
5
6use crate::{prelude::ChildMonitor, work::Work, CloseError};
7
8use super::{ProgressMonitor, ProgressMonitorDivision};
9
10pub struct CallbackProgressMonitor<'n, W: Work, C: Fn(&W, &W)> {
11    name: Cow<'n, str>,
12    work: W,
13    work_done: W,
14    callback: C,
15    closed: Option<Result<(), CloseError>>,
16}
17
18impl<'n, W, C> Debug for CallbackProgressMonitor<'n, W, C>
19where
20    W: Work,
21    C: Fn(&W, &W),
22{
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        f.debug_struct("CallbackProgressMonitor")
25            .field("name", &self.name)
26            .field("work", &self.work)
27            .field("work_done", &self.work_done)
28            .finish()
29    }
30}
31
32impl<'n, W, C> CallbackProgressMonitor<'n, W, C>
33where
34    W: Work,
35    C: Fn(&W, &W),
36{
37    pub fn new<N: Into<Cow<'n, str>>, A: Into<W>>(name: N, work: A, callback: C) -> Self {
38        Self {
39            name: name.into(),
40            work: work.into(),
41            work_done: W::zero(),
42            callback,
43            closed: None,
44        }
45    }
46}
47
48impl<'n, W, C> ProgressMonitor<W> for CallbackProgressMonitor<'n, W, C>
49where
50    W: Work,
51    C: Fn(&W, &W),
52{
53    fn worked<A: Into<W>>(&mut self, amount: A) {
54        let amount: W = amount.into();
55        let now: W = (self.work_done.clone() + amount.clone()).expect("Addition to work"); // TODO: Handle error!?
56        if now > self.work {
57            // TODO: Control overshoot behavior through monitor configuration.
58            tracing::warn!(
59                work = ?self.work,
60                work_done = ?self.work_done,
61                new_work_done = ?amount,
62                would_become = ?now,
63                "Detected overshoot. Try to only submit work left open. Ignoring additional work."
64            );
65            self.work_done = self.work.clone();
66        } else {
67            self.work_done = now;
68        }
69        (self.callback)(&self.work, &self.work_done);
70    }
71
72    /// Get the total amount of work.
73    fn total(&self) -> &W {
74        &self.work
75    }
76
77    /// Get the amount of work completed.
78    fn completed(&self) -> &W {
79        &self.work_done
80    }
81
82    /// Get the amount of work remaining.
83    fn remaining(&self) -> Cow<W> {
84        Cow::Owned(self.work.clone() - self.work_done.clone())
85    }
86
87    fn close(&mut self) -> Result<(), crate::CloseError> {
88        let work_left = self.remaining();
89        let result = if work_left.as_ref() == &W::zero() {
90            Ok(())
91        } else {
92            Err(crate::CloseError {
93                msg: format!(
94                    "Must not drop progress monitor {self:#?} when work left is {work_left}."
95                ),
96            })
97        };
98        self.closed = Some(result.clone());
99        result
100    }
101}
102
103impl<'p, 'n, N, W, A1, A2, C> ProgressMonitorDivision<'p, 'n, N, W, A1, A2>
104    for CallbackProgressMonitor<'n, W, C>
105where
106    N: Into<Cow<'n, str>>,
107    W: Work,
108    A1: Into<W>,
109    A2: Into<W>,
110    C: Fn(&W, &W),
111{
112    fn new_child(
113        &'p mut self,
114        name: N,
115        parent_work: A1,
116        child_work: A2,
117    ) -> ChildMonitor<'n, 'p, W, Self> {
118        let parent_work: W = parent_work.into();
119        let total_child_work: W = child_work.into();
120
121        // TODO: As Result?
122        assert!(&parent_work <= self.remaining().as_ref());
123
124        ChildMonitor::new(name.into(), self, parent_work, total_child_work)
125    }
126}
127
128impl<'n, W: Work, C: Fn(&W, &W)> Display for CallbackProgressMonitor<'n, W, C> {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        f.write_fmt(format_args!("{}/{}", self.work_done, self.work))
131    }
132}
133
134impl<'n, W: Work, C: Fn(&W, &W)> Drop for CallbackProgressMonitor<'n, W, C> {
135    fn drop(&mut self) {
136        match &self.closed {
137            Some(result) => {
138                assert!(result.is_ok());
139            }
140            None => {
141                tracing::warn!("close() was not called on {self:?}!");
142                self.close().expect("Successful close");
143            }
144        }
145    }
146}