progress_monitor/monitor/
callback.rs1use 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"); if now > self.work {
57 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 fn total(&self) -> &W {
74 &self.work
75 }
76
77 fn completed(&self) -> &W {
79 &self.work_done
80 }
81
82 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 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}