aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_progress/src/lib.rs
blob: 0ff1f846ca1b9726362b029e62f45288b5b7d102 (plain)
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
//! General-purpose instrumentation for progress reporting.
//!
//! Note:
//! Most of the methods accept `&mut self` just to be more restrictive (for forward compat)
//! even tho for some of them we can weaken this requirement to shared reference (`&self`).

use crossbeam_channel::Receiver;
use std::fmt;

#[derive(Debug)]
pub enum ProgressStatus<B, P> {
    Begin(B),
    Progress(P),
    End,
}

pub struct Progress<B, P>(Option<crossbeam_channel::Sender<ProgressStatus<B, P>>>);
impl<B, P> Progress<B, P> {
    pub fn report(&mut self, payload: P) {
        self.report_with(|| payload);
    }

    pub fn report_with(&mut self, payload: impl FnOnce() -> P) {
        self.send_status(|| ProgressStatus::Progress(payload()));
    }

    fn send_status(&self, status: impl FnOnce() -> ProgressStatus<B, P>) {
        if let Some(sender) = &self.0 {
            sender.try_send(status()).expect("progress report must not block");
        }
    }
}

impl<B, P> Drop for Progress<B, P> {
    fn drop(&mut self) {
        self.send_status(|| ProgressStatus::End);
    }
}

pub struct ProgressSource<B, P>(Option<crossbeam_channel::Sender<ProgressStatus<B, P>>>);
impl<B, P> ProgressSource<B, P> {
    pub fn real_if(real: bool) -> (Receiver<ProgressStatus<B, P>>, Self) {
        if real {
            let (sender, receiver) = crossbeam_channel::unbounded();
            (receiver, Self(Some(sender)))
        } else {
            (crossbeam_channel::never(), Self(None))
        }
    }

    pub fn begin(&mut self, payload: B) -> Progress<B, P> {
        self.begin_with(|| payload)
    }

    pub fn begin_with(&mut self, payload: impl FnOnce() -> B) -> Progress<B, P> {
        let progress = Progress(self.0.clone());
        progress.send_status(|| ProgressStatus::Begin(payload()));
        progress
    }
}

impl<B, P> Clone for ProgressSource<B, P> {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

impl<B, P> fmt::Debug for ProgressSource<B, P> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("ProgressSource").field(&self.0).finish()
    }
}

pub type U32ProgressStatus = ProgressStatus<U32ProgressReport, U32ProgressReport>;

#[derive(Debug)]
pub struct U32ProgressReport {
    pub processed: u32,
    pub total: u32,
}
impl U32ProgressReport {
    pub fn percentage(&self) -> f64 {
        f64::from(100 * self.processed) / f64::from(self.total)
    }
    pub fn to_message(&self, prefix: &str, unit: &str) -> String {
        format!("{} ({}/{} {})", prefix, self.processed, self.total, unit)
    }
}

pub struct U32Progress {
    inner: Progress<U32ProgressReport, U32ProgressReport>,
    processed: u32,
    total: u32,
}

#[derive(Debug, Eq, PartialEq)]
pub struct IsDone(pub bool);

impl U32Progress {
    pub fn report(&mut self, new_processed: u32) -> IsDone {
        if self.processed < new_processed {
            self.processed = new_processed;
            self.inner.report(U32ProgressReport { processed: new_processed, total: self.total });
        }
        IsDone(self.processed >= self.total)
    }
}

#[derive(Clone)]
pub struct U32ProgressSource {
    inner: ProgressSource<U32ProgressReport, U32ProgressReport>,
}

impl U32ProgressSource {
    pub fn real_if(
        real: bool,
    ) -> (Receiver<ProgressStatus<U32ProgressReport, U32ProgressReport>>, Self) {
        let (recv, inner) = ProgressSource::real_if(real);
        (recv, Self { inner })
    }

    pub fn begin(&mut self, initial: u32, total: u32) -> U32Progress {
        U32Progress {
            inner: self.inner.begin(U32ProgressReport { processed: initial, total }),
            processed: initial,
            total,
        }
    }
}