1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::{
    borrow::Cow,
    fmt::{Debug, Display},
};

use crate::{prelude::ChildMonitor, work::Work, CloseError};

use super::{ProgressMonitor, ProgressMonitorDivision};

pub struct CallbackProgressMonitor<'n, W: Work, C: Fn(&W, &W)> {
    name: Cow<'n, str>,
    work: W,
    work_done: W,
    callback: C,
    closed: Option<Result<(), CloseError>>,
}

impl<'n, W, C> Debug for CallbackProgressMonitor<'n, W, C>
where
    W: Work,
    C: Fn(&W, &W),
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("CallbackProgressMonitor")
            .field("name", &self.name)
            .field("work", &self.work)
            .field("work_done", &self.work_done)
            .finish()
    }
}

impl<'n, W, C> CallbackProgressMonitor<'n, W, C>
where
    W: Work,
    C: Fn(&W, &W),
{
    pub fn new<N: Into<Cow<'n, str>>, A: Into<W>>(name: N, work: A, callback: C) -> Self {
        Self {
            name: name.into(),
            work: work.into(),
            work_done: W::zero(),
            callback,
            closed: None,
        }
    }
}

impl<'n, W, C> ProgressMonitor<W> for CallbackProgressMonitor<'n, W, C>
where
    W: Work,
    C: Fn(&W, &W),
{
    fn worked<A: Into<W>>(&mut self, amount: A) {
        let now = (self.work_done.clone() + amount.into()).unwrap(); // TODO: Handle error!
        if now > self.work {
            // TOOD: Handle overshoot: e.g. require Min trait implementation, return min(possibly_overshootet, max_work)
            println!("now: {now:?}");
            println!("work: {:?}", self.work);
            panic!("");
        }
        self.work_done = now;
        (self.callback)(&self.work, &self.work_done);
    }

    /// Get the total amount of work.
    fn total(&self) -> &W {
        &self.work
    }

    /// Get the amount of work completed.
    fn completed(&self) -> &W {
        &self.work_done
    }

    /// Get the amount of work remaining.
    fn remaining(&self) -> Cow<W> {
        Cow::Owned(self.work.clone() - self.work_done.clone())
    }

    fn close(&mut self) -> Result<(), crate::CloseError> {
        let work_left = self.remaining();
        let result = if work_left.as_ref() == &W::zero() {
            Ok(())
        } else {
            Err(crate::CloseError {
                msg: format!(
                    "Must not drop progress monitor {self:#?} when work left is {work_left}."
                ),
            })
        };
        self.closed = Some(result.clone());
        result
    }
}

impl<'p, 'n, N, W, A1, A2, C> ProgressMonitorDivision<'p, 'n, N, W, A1, A2>
    for CallbackProgressMonitor<'n, W, C>
where
    N: Into<Cow<'n, str>>,
    W: Work,
    A1: Into<W>,
    A2: Into<W>,
    C: Fn(&W, &W),
{
    fn new_child(
        &'p mut self,
        name: N,
        parent_work: A1,
        child_work: A2,
    ) -> ChildMonitor<'n, 'p, W, Self> {
        let parent_work: W = parent_work.into();
        let total_child_work: W = child_work.into();

        // TODO: As Result?
        assert!(&parent_work <= self.remaining().as_ref());

        ChildMonitor::new(name.into(), self, parent_work, total_child_work)
    }
}

impl<'n, W: Work, C: Fn(&W, &W)> Display for CallbackProgressMonitor<'n, W, C> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!("{}/{}", self.work_done, self.work))
    }
}

impl<'n, W: Work, C: Fn(&W, &W)> Drop for CallbackProgressMonitor<'n, W, C> {
    fn drop(&mut self) {
        match &self.closed {
            Some(result) => {
                assert!(result.is_ok());
            }
            None => {
                tracing::warn!("close() was not called on {self:?}!");
                self.close().expect("Successful close");
            }
        }
    }
}