aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_progress/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_progress/src')
-rw-r--r--crates/ra_progress/src/lib.rs129
1 files changed, 129 insertions, 0 deletions
diff --git a/crates/ra_progress/src/lib.rs b/crates/ra_progress/src/lib.rs
new file mode 100644
index 000000000..0ff1f846c
--- /dev/null
+++ b/crates/ra_progress/src/lib.rs
@@ -0,0 +1,129 @@
1//! General-purpose instrumentation for progress reporting.
2//!
3//! Note:
4//! Most of the methods accept `&mut self` just to be more restrictive (for forward compat)
5//! even tho for some of them we can weaken this requirement to shared reference (`&self`).
6
7use crossbeam_channel::Receiver;
8use std::fmt;
9
10#[derive(Debug)]
11pub enum ProgressStatus<B, P> {
12 Begin(B),
13 Progress(P),
14 End,
15}
16
17pub struct Progress<B, P>(Option<crossbeam_channel::Sender<ProgressStatus<B, P>>>);
18impl<B, P> Progress<B, P> {
19 pub fn report(&mut self, payload: P) {
20 self.report_with(|| payload);
21 }
22
23 pub fn report_with(&mut self, payload: impl FnOnce() -> P) {
24 self.send_status(|| ProgressStatus::Progress(payload()));
25 }
26
27 fn send_status(&self, status: impl FnOnce() -> ProgressStatus<B, P>) {
28 if let Some(sender) = &self.0 {
29 sender.try_send(status()).expect("progress report must not block");
30 }
31 }
32}
33
34impl<B, P> Drop for Progress<B, P> {
35 fn drop(&mut self) {
36 self.send_status(|| ProgressStatus::End);
37 }
38}
39
40pub struct ProgressSource<B, P>(Option<crossbeam_channel::Sender<ProgressStatus<B, P>>>);
41impl<B, P> ProgressSource<B, P> {
42 pub fn real_if(real: bool) -> (Receiver<ProgressStatus<B, P>>, Self) {
43 if real {
44 let (sender, receiver) = crossbeam_channel::unbounded();
45 (receiver, Self(Some(sender)))
46 } else {
47 (crossbeam_channel::never(), Self(None))
48 }
49 }
50
51 pub fn begin(&mut self, payload: B) -> Progress<B, P> {
52 self.begin_with(|| payload)
53 }
54
55 pub fn begin_with(&mut self, payload: impl FnOnce() -> B) -> Progress<B, P> {
56 let progress = Progress(self.0.clone());
57 progress.send_status(|| ProgressStatus::Begin(payload()));
58 progress
59 }
60}
61
62impl<B, P> Clone for ProgressSource<B, P> {
63 fn clone(&self) -> Self {
64 Self(self.0.clone())
65 }
66}
67
68impl<B, P> fmt::Debug for ProgressSource<B, P> {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 f.debug_tuple("ProgressSource").field(&self.0).finish()
71 }
72}
73
74pub type U32ProgressStatus = ProgressStatus<U32ProgressReport, U32ProgressReport>;
75
76#[derive(Debug)]
77pub struct U32ProgressReport {
78 pub processed: u32,
79 pub total: u32,
80}
81impl U32ProgressReport {
82 pub fn percentage(&self) -> f64 {
83 f64::from(100 * self.processed) / f64::from(self.total)
84 }
85 pub fn to_message(&self, prefix: &str, unit: &str) -> String {
86 format!("{} ({}/{} {})", prefix, self.processed, self.total, unit)
87 }
88}
89
90pub struct U32Progress {
91 inner: Progress<U32ProgressReport, U32ProgressReport>,
92 processed: u32,
93 total: u32,
94}
95
96#[derive(Debug, Eq, PartialEq)]
97pub struct IsDone(pub bool);
98
99impl U32Progress {
100 pub fn report(&mut self, new_processed: u32) -> IsDone {
101 if self.processed < new_processed {
102 self.processed = new_processed;
103 self.inner.report(U32ProgressReport { processed: new_processed, total: self.total });
104 }
105 IsDone(self.processed >= self.total)
106 }
107}
108
109#[derive(Clone)]
110pub struct U32ProgressSource {
111 inner: ProgressSource<U32ProgressReport, U32ProgressReport>,
112}
113
114impl U32ProgressSource {
115 pub fn real_if(
116 real: bool,
117 ) -> (Receiver<ProgressStatus<U32ProgressReport, U32ProgressReport>>, Self) {
118 let (recv, inner) = ProgressSource::real_if(real);
119 (recv, Self { inner })
120 }
121
122 pub fn begin(&mut self, initial: u32, total: u32) -> U32Progress {
123 U32Progress {
124 inner: self.inner.begin(U32ProgressReport { processed: initial, total }),
125 processed: initial,
126 total,
127 }
128 }
129}