barber/progress_bar.rs
1use std::sync::{
2 Arc,
3 atomic::{AtomicUsize, Ordering},
4 mpsc::channel,
5};
6
7use crate::{ProgressEvent, ProgressHandle, ProgressRenderer, spawn_progress_thread};
8
9/// A thread safe, thread stable progress bar, abstracted for any frontend
10#[derive(Clone)]
11pub struct ProgressBar {
12 handle: Arc<ProgressHandle>,
13}
14
15impl ProgressBar {
16 /// Runs a custom closure for this progress bar.
17 ///
18 /// # Panics
19 ///
20 /// Panics if the internal `GLOBAL_SENDER` thread cannot be communicated with. This usually signals critical failure somewhere
21 ///
22 /// # Notes
23 ///
24 /// If the input closure panics, it will not kill the thread, it will instead log the error and return
25 pub fn custom<F>(&self, f: F)
26 where
27 F: FnOnce() + Send + 'static,
28 {
29 if self.handle.renderer.__is_null() {
30 return;
31 }
32
33 self.handle
34 .sender
35 .send(ProgressEvent::Custom {
36 f: Box::new(f),
37 _handle: self.handle.clone(),
38 })
39 .expect("Progress handler thread died.");
40 }
41
42 /// Waits until the flush is processed, allowing you to wait until all previous events have been processed
43 ///
44 /// # Panics
45 ///
46 /// Panics if the internal `GLOBAL_SENDER` thread cannot be communicated with. This usually signals critical failure somewhere
47 pub fn flush(&self) {
48 if self.handle.renderer.__is_null() {
49 return;
50 }
51
52 let (tx, rx) = channel();
53 self.handle
54 .sender
55 .send(ProgressEvent::Flush {
56 done: tx,
57 _handle: self.handle.clone(),
58 })
59 .expect("Progress handler thread died.");
60 let _ = rx.recv();
61 }
62
63 /// Gets the max value of the internal `ProgressHandle`
64 ///
65 /// # Notes
66 ///
67 /// While this will return the correct max value at time of call, In multi-threaded contexts, this max may have already changed by the time it has been used
68 pub fn get_max(&self) -> usize {
69 self.handle.max.load(Ordering::SeqCst)
70 }
71
72 /// Gets the value of the internal `ProgressHandle`
73 ///
74 /// # Notes
75 ///
76 /// While this will return the correct value at time of call, In multi-threaded contexts, this value may have already changed by the time it has been used
77 pub fn get_value(&self) -> usize {
78 self.handle.value.load(Ordering::SeqCst)
79 }
80
81 /// Increments the value of a progress bar by 1
82 ///
83 /// # Panics
84 ///
85 /// Panics if the internal `GLOBAL_SENDER` thread cannot be communicated with. This usually signals critical failure somewhere
86 pub fn increment(&self) {
87 if self.handle.renderer.__is_null() {
88 return;
89 }
90
91 self.handle
92 .sender
93 .send(ProgressEvent::Increment {
94 handle: self.handle.clone(),
95 })
96 .expect("Progress handler thread died.");
97 }
98
99 /// Creates a new progress bar instance using the renderer
100 ///
101 /// This progress bar can safely be shared and updated between threads
102 pub fn new(value: usize, max: usize, renderer: Arc<dyn ProgressRenderer>) -> Self {
103 let (tx, rx) = channel();
104
105 let handle = Arc::new(ProgressHandle {
106 value: AtomicUsize::new(value),
107 max: AtomicUsize::new(max),
108 renderer: renderer.clone(),
109 sender: tx,
110 });
111
112 if !renderer.__is_null() {
113 spawn_progress_thread(rx);
114 renderer.on_start(value, max);
115 }
116
117 Self { handle }
118 }
119
120 /// Sets the max-value of the progress bar
121 ///
122 /// # Panics
123 ///
124 /// Panics if the internal `GLOBAL_SENDER` thread cannot be communicated with. This usually signals critical failure somewhere
125 pub fn set_max(&self, max: usize) {
126 if self.handle.renderer.__is_null() {
127 return;
128 }
129
130 self.handle
131 .sender
132 .send(ProgressEvent::SetMax {
133 handle: self.handle.clone(),
134 max,
135 })
136 .expect("Progress handler thread died.");
137 }
138
139 /// Sets the value of the progress bar
140 ///
141 /// # Panics
142 ///
143 /// Panics if the internal `GLOBAL_SENDER` thread cannot be communicated with. This usually signals critical failure somewhere
144 pub fn set_value(&self, value: usize) {
145 if self.handle.renderer.__is_null() {
146 return;
147 }
148
149 self.handle
150 .sender
151 .send(ProgressEvent::SetValue {
152 handle: self.handle.clone(),
153 value,
154 })
155 .expect("Progress handler thread died.");
156 }
157
158 /// Forces the [`ProgressRenderer`] to upate with the new values
159 ///
160 /// # Panics
161 ///
162 /// Panics if the progress bars handling thread cannot be communicated with.
163 pub fn update(&self) {
164 if self.handle.renderer.__is_null() {
165 return;
166 }
167
168 self.handle
169 .sender
170 .send(ProgressEvent::Update {
171 handle: self.handle.clone(),
172 })
173 .expect("Progress handler thread died.")
174 }
175
176 /// Checks if the the handling thread is alive by sending a check to it and seeing if it returns [`Ok`]
177 ///
178 /// # Notes
179 ///
180 /// This function will correctly check if the handling thread is alive at the time of calling, but can not guarantee the thread will stay alive after calling or that it receives the check
181 pub fn is_alive(&self) -> bool {
182 if self.handle.renderer.__is_null() {
183 return false;
184 }
185
186 self.handle.sender.send(ProgressEvent::Check).is_ok()
187 }
188
189 /// Calls the [`ProgressRenderer::set_label()`] function for the held [`ProgressRenderer`]
190 pub fn set_label(&self, label: &str) {
191 self.handle.renderer.set_label(label);
192 }
193
194 /// Tells the handling thread to call the [`ProgressRenderer::on_notify()`] function for the held [`ProgressRenderer`]
195 ///
196 /// # Panics
197 ///
198 /// Panics if the progress bars handling thread cannot be communicated with.
199 pub fn notify(&self, message: impl ToString) {
200 if self.handle.renderer.__is_null() {
201 return;
202 }
203
204 self.handle
205 .sender
206 .send(ProgressEvent::Notify {
207 handle: self.handle.clone(),
208 msg: message.to_string(),
209 })
210 .expect("Progress handler thread died.")
211 }
212}