diff options
Diffstat (limited to 'crates/ra_progress')
-rw-r--r-- | crates/ra_progress/Cargo.toml | 8 | ||||
-rw-r--r-- | crates/ra_progress/src/lib.rs | 129 |
2 files changed, 137 insertions, 0 deletions
diff --git a/crates/ra_progress/Cargo.toml b/crates/ra_progress/Cargo.toml new file mode 100644 index 000000000..c7f7c6dd3 --- /dev/null +++ b/crates/ra_progress/Cargo.toml | |||
@@ -0,0 +1,8 @@ | |||
1 | [package] | ||
2 | name = "ra_progress" | ||
3 | version = "0.1.0" | ||
4 | authors = ["rust-analyzer developers"] | ||
5 | edition = "2018" | ||
6 | |||
7 | [dependencies] | ||
8 | crossbeam-channel = { version = "0.4" } | ||
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 | |||
7 | use crossbeam_channel::Receiver; | ||
8 | use std::fmt; | ||
9 | |||
10 | #[derive(Debug)] | ||
11 | pub enum ProgressStatus<B, P> { | ||
12 | Begin(B), | ||
13 | Progress(P), | ||
14 | End, | ||
15 | } | ||
16 | |||
17 | pub struct Progress<B, P>(Option<crossbeam_channel::Sender<ProgressStatus<B, P>>>); | ||
18 | impl<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 | |||
34 | impl<B, P> Drop for Progress<B, P> { | ||
35 | fn drop(&mut self) { | ||
36 | self.send_status(|| ProgressStatus::End); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | pub struct ProgressSource<B, P>(Option<crossbeam_channel::Sender<ProgressStatus<B, P>>>); | ||
41 | impl<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 | |||
62 | impl<B, P> Clone for ProgressSource<B, P> { | ||
63 | fn clone(&self) -> Self { | ||
64 | Self(self.0.clone()) | ||
65 | } | ||
66 | } | ||
67 | |||
68 | impl<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 | |||
74 | pub type U32ProgressStatus = ProgressStatus<U32ProgressReport, U32ProgressReport>; | ||
75 | |||
76 | #[derive(Debug)] | ||
77 | pub struct U32ProgressReport { | ||
78 | pub processed: u32, | ||
79 | pub total: u32, | ||
80 | } | ||
81 | impl 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 | |||
90 | pub struct U32Progress { | ||
91 | inner: Progress<U32ProgressReport, U32ProgressReport>, | ||
92 | processed: u32, | ||
93 | total: u32, | ||
94 | } | ||
95 | |||
96 | #[derive(Debug, Eq, PartialEq)] | ||
97 | pub struct IsDone(pub bool); | ||
98 | |||
99 | impl 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)] | ||
110 | pub struct U32ProgressSource { | ||
111 | inner: ProgressSource<U32ProgressReport, U32ProgressReport>, | ||
112 | } | ||
113 | |||
114 | impl 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 | } | ||