1 use std::borrow::Cow;
2 use std::fmt;
3 use std::io;
4 use std::sync::atomic::{AtomicBool, Ordering};
5 use std::sync::mpsc::{channel, Receiver, Sender};
6 use std::sync::{Arc, Mutex, RwLock, Weak};
7 use std::thread;
8 use std::time::{Duration, Instant};
9 
10 use crate::state::{
11     MultiObject, MultiProgressState, ProgressDrawState, ProgressDrawTarget, ProgressDrawTargetKind,
12     ProgressState, Status,
13 };
14 use crate::style::ProgressStyle;
15 use crate::utils::Estimate;
16 use crate::{ProgressBarIter, ProgressIterator};
17 
18 /// A progress bar or spinner
19 ///
20 /// The progress bar is an [`Arc`] around its internal state. When the progress bar is cloned it
21 /// just increments the refcount (so the original and its clone share the same state).
22 #[derive(Clone)]
23 pub struct ProgressBar {
24     state: Arc<Mutex<ProgressState>>,
25 }
26 
27 impl fmt::Debug for ProgressBar {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result28     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29         f.debug_struct("ProgressBar").finish()
30     }
31 }
32 
33 impl ProgressBar {
34     /// Creates a new progress bar with a given length
35     ///
36     /// This progress bar by default draws directly to stderr, and refreshes a maximum of 15 times
37     /// a second. To change the refresh rate, set the draw target to one with a different refresh
38     /// rate.
new(len: u64) -> ProgressBar39     pub fn new(len: u64) -> ProgressBar {
40         ProgressBar::with_draw_target(len, ProgressDrawTarget::stderr())
41     }
42 
43     /// Creates a completely hidden progress bar
44     ///
45     /// This progress bar still responds to API changes but it does not have a length or render in
46     /// any way.
hidden() -> ProgressBar47     pub fn hidden() -> ProgressBar {
48         ProgressBar::with_draw_target(!0, ProgressDrawTarget::hidden())
49     }
50 
51     /// Creates a new progress bar with a given length and draw target
with_draw_target(len: u64, target: ProgressDrawTarget) -> ProgressBar52     pub fn with_draw_target(len: u64, target: ProgressDrawTarget) -> ProgressBar {
53         ProgressBar {
54             state: Arc::new(Mutex::new(ProgressState {
55                 style: ProgressStyle::default_bar(),
56                 draw_target: target,
57                 message: "".into(),
58                 prefix: "".into(),
59                 pos: 0,
60                 len,
61                 tick: 0,
62                 draw_delta: 0,
63                 draw_rate: 0,
64                 draw_next: 0,
65                 status: Status::InProgress,
66                 started: Instant::now(),
67                 est: Estimate::new(),
68                 tick_thread: None,
69                 steady_tick: 0,
70             })),
71         }
72     }
73 
74     /// A convenience builder-like function for a progress bar with a given style
with_style(self, style: ProgressStyle) -> ProgressBar75     pub fn with_style(self, style: ProgressStyle) -> ProgressBar {
76         self.state.lock().unwrap().style = style;
77         self
78     }
79 
80     /// A convenience builder-like function for a progress bar with a given prefix
with_prefix(self, prefix: impl Into<Cow<'static, str>>) -> ProgressBar81     pub fn with_prefix(self, prefix: impl Into<Cow<'static, str>>) -> ProgressBar {
82         self.state.lock().unwrap().prefix = prefix.into();
83         self
84     }
85 
86     /// A convenience builder-like function for a progress bar with a given message
with_message(self, message: impl Into<Cow<'static, str>>) -> ProgressBar87     pub fn with_message(self, message: impl Into<Cow<'static, str>>) -> ProgressBar {
88         self.state.lock().unwrap().message = message.into();
89         self
90     }
91 
92     /// A convenience builder-like function for a progress bar with a given position
with_position(self, pos: u64) -> ProgressBar93     pub fn with_position(self, pos: u64) -> ProgressBar {
94         self.state.lock().unwrap().pos = pos;
95         self
96     }
97 
98     /// Creates a new spinner
99     ///
100     /// This spinner by default draws directly to stderr. This adds the default spinner style to it.
new_spinner() -> ProgressBar101     pub fn new_spinner() -> ProgressBar {
102         let rv = ProgressBar::new(!0);
103         rv.set_style(ProgressStyle::default_spinner());
104         rv
105     }
106 
107     /// Overrides the stored style
108     ///
109     /// This does not redraw the bar. Call [`ProgressBar::tick()`] to force it.
set_style(&self, style: ProgressStyle)110     pub fn set_style(&self, style: ProgressStyle) {
111         self.state.lock().unwrap().style = style;
112     }
113 
114     /// Spawns a background thread to tick the progress bar
115     ///
116     /// When this is enabled a background thread will regularly tick the progress bar in the given
117     /// interval (in milliseconds). This is useful to advance progress bars that are very slow by
118     /// themselves.
119     ///
120     /// When steady ticks are enabled, calling [`ProgressBar::tick()`] on a progress bar does not
121     /// have any effect.
enable_steady_tick(&self, ms: u64)122     pub fn enable_steady_tick(&self, ms: u64) {
123         let mut state = self.state.lock().unwrap();
124         state.steady_tick = ms;
125         if state.tick_thread.is_some() {
126             return;
127         }
128 
129         // Using a weak pointer is required to prevent a potential deadlock. See issue #133
130         let state_arc = Arc::downgrade(&self.state);
131         state.tick_thread = Some(thread::spawn(move || Self::steady_tick(state_arc, ms)));
132 
133         ::std::mem::drop(state);
134         // use the side effect of tick to force the bar to tick.
135         self.tick();
136     }
137 
steady_tick(state_arc: Weak<Mutex<ProgressState>>, mut ms: u64)138     fn steady_tick(state_arc: Weak<Mutex<ProgressState>>, mut ms: u64) {
139         loop {
140             thread::sleep(Duration::from_millis(ms));
141             if let Some(state_arc) = state_arc.upgrade() {
142                 let mut state = state_arc.lock().unwrap();
143                 if state.is_finished() || state.steady_tick == 0 {
144                     state.steady_tick = 0;
145                     state.tick_thread = None;
146                     break;
147                 }
148                 if state.tick != 0 {
149                     state.tick = state.tick.saturating_add(1);
150                 }
151                 ms = state.steady_tick;
152 
153                 state.draw().ok();
154             } else {
155                 break;
156             }
157         }
158     }
159 
160     /// Undoes [`ProgressBar::enable_steady_tick()`]
disable_steady_tick(&self)161     pub fn disable_steady_tick(&self) {
162         self.enable_steady_tick(0);
163     }
164 
165     /// Limit redrawing of progress bar to every `n` steps
166     ///
167     /// By default, the progress bar will redraw whenever its state advances. This setting is
168     /// helpful in situations where the overhead of redrawing the progress bar dominates the
169     /// computation whose progress is being reported.
170     ///
171     /// If `n` is greater than 0, operations that change the progress bar such as
172     /// [`ProgressBar::tick()`], [`ProgressBar::set_message()`] and [`ProgressBar::set_length()`]
173     /// will no longer cause the progress bar to be redrawn, and will only be shown once the
174     /// position advances by `n` steps.
175     ///
176     /// ```rust,no_run
177     /// # use indicatif::ProgressBar;
178     /// let n = 1_000_000;
179     /// let pb = ProgressBar::new(n);
180     /// pb.set_draw_delta(n / 100); // redraw every 1% of additional progress
181     /// ```
182     ///
183     /// Note that `ProgressDrawTarget` may impose additional buffering of redraws.
set_draw_delta(&self, n: u64)184     pub fn set_draw_delta(&self, n: u64) {
185         let mut state = self.state.lock().unwrap();
186         state.draw_delta = n;
187         state.draw_next = state.pos.saturating_add(state.draw_delta);
188     }
189 
190     /// Sets the refresh rate of progress bar to `n` updates per seconds
191     ///
192     /// This is similar to `set_draw_delta` but automatically adapts to a constant refresh rate
193     /// regardless of how consistent the progress is.
194     ///
195     /// This parameter takes precedence on `set_draw_delta` if different from 0.
196     ///
197     /// ```rust,no_run
198     /// # use indicatif::ProgressBar;
199     /// let n = 1_000_000;
200     /// let pb = ProgressBar::new(n);
201     /// pb.set_draw_rate(25); // aims at redrawing at most 25 times per seconds.
202     /// ```
203     ///
204     /// Note that the [`ProgressDrawTarget`] may impose additional buffering of redraws.
set_draw_rate(&self, n: u64)205     pub fn set_draw_rate(&self, n: u64) {
206         let mut state = self.state.lock().unwrap();
207         state.draw_rate = n;
208         state.draw_next = state.pos.saturating_add(state.per_sec() / n);
209     }
210 
211     /// Manually ticks the spinner or progress bar
212     ///
213     /// This automatically happens on any other change to a progress bar.
tick(&self)214     pub fn tick(&self) {
215         self.update_and_draw(|state| {
216             if state.steady_tick == 0 || state.tick == 0 {
217                 state.tick = state.tick.saturating_add(1);
218             }
219         });
220     }
221 
222     /// Advances the position of the progress bar by `delta`
inc(&self, delta: u64)223     pub fn inc(&self, delta: u64) {
224         self.update_and_draw(|state| {
225             state.pos = state.pos.saturating_add(delta);
226             if state.steady_tick == 0 || state.tick == 0 {
227                 state.tick = state.tick.saturating_add(1);
228             }
229         })
230     }
231 
232     /// A quick convenience check if the progress bar is hidden
is_hidden(&self) -> bool233     pub fn is_hidden(&self) -> bool {
234         self.state.lock().unwrap().draw_target.is_hidden()
235     }
236 
237     /// Indicates that the progress bar finished
is_finished(&self) -> bool238     pub fn is_finished(&self) -> bool {
239         self.state.lock().unwrap().is_finished()
240     }
241 
242     /// Print a log line above the progress bar
243     ///
244     /// If the progress bar was added to a [`MultiProgress`], the log line will be
245     /// printed above all other progress bars.
246     ///
247     /// Note that if the progress bar is hidden (which by default happens if the progress bar is
248     /// redirected into a file) `println()` will not do anything either.
println<I: AsRef<str>>(&self, msg: I)249     pub fn println<I: AsRef<str>>(&self, msg: I) {
250         let mut state = self.state.lock().unwrap();
251 
252         let mut lines: Vec<String> = msg.as_ref().lines().map(Into::into).collect();
253         let orphan_lines = lines.len();
254         if state.should_render() && !state.draw_target.is_hidden() {
255             lines.extend(state.style.format_state(&*state));
256         }
257 
258         let draw_state = ProgressDrawState {
259             lines,
260             orphan_lines,
261             finished: state.is_finished(),
262             force_draw: true,
263             move_cursor: false,
264         };
265 
266         state.draw_target.apply_draw_state(draw_state).ok();
267     }
268 
269     /// Sets the position of the progress bar
set_position(&self, pos: u64)270     pub fn set_position(&self, pos: u64) {
271         self.update_and_draw(|state| {
272             state.draw_next = pos;
273             state.pos = pos;
274             if state.steady_tick == 0 || state.tick == 0 {
275                 state.tick = state.tick.saturating_add(1);
276             }
277         })
278     }
279 
280     /// Sets the length of the progress bar
set_length(&self, len: u64)281     pub fn set_length(&self, len: u64) {
282         self.update_and_draw(|state| {
283             state.len = len;
284         })
285     }
286 
287     /// Increase the length of the progress bar
inc_length(&self, delta: u64)288     pub fn inc_length(&self, delta: u64) {
289         self.update_and_draw(|state| {
290             state.len = state.len.saturating_add(delta);
291         })
292     }
293 
294     /// Sets the current prefix of the progress bar
295     ///
296     /// For the prefix to be visible, the `{prefix}` placeholder must be present in the template
297     /// (see [`ProgressStyle`]).
set_prefix(&self, prefix: impl Into<Cow<'static, str>>)298     pub fn set_prefix(&self, prefix: impl Into<Cow<'static, str>>) {
299         let prefix = prefix.into();
300         self.update_and_draw(|state| {
301             state.prefix = prefix;
302             if state.steady_tick == 0 || state.tick == 0 {
303                 state.tick = state.tick.saturating_add(1);
304             }
305         })
306     }
307 
308     /// Sets the current message of the progress bar
309     ///
310     /// For the message to be visible, the `{msg}` placeholder must be present in the template (see
311     /// [`ProgressStyle`]).
set_message(&self, msg: impl Into<Cow<'static, str>>)312     pub fn set_message(&self, msg: impl Into<Cow<'static, str>>) {
313         let msg = msg.into();
314         self.update_and_draw(|state| {
315             state.message = msg;
316             if state.steady_tick == 0 || state.tick == 0 {
317                 state.tick = state.tick.saturating_add(1);
318             }
319         })
320     }
321 
322     /// Creates a new weak reference to this `ProgressBar`
downgrade(&self) -> WeakProgressBar323     pub fn downgrade(&self) -> WeakProgressBar {
324         WeakProgressBar {
325             state: Arc::downgrade(&self.state),
326         }
327     }
328 
329     /// Resets the ETA calculation
330     ///
331     /// This can be useful if the progress bars made a large jump or was paused for a prolonged
332     /// time.
reset_eta(&self)333     pub fn reset_eta(&self) {
334         self.update_and_draw(|state| {
335             state.est.reset(state.pos);
336         });
337     }
338 
339     /// Resets elapsed time
reset_elapsed(&self)340     pub fn reset_elapsed(&self) {
341         self.update_and_draw(|state| {
342             state.started = Instant::now();
343         });
344     }
345 
346     /// Resets all of the progress bar state
reset(&self)347     pub fn reset(&self) {
348         self.reset_eta();
349         self.reset_elapsed();
350         self.update_and_draw(|state| {
351             state.draw_next = 0;
352             state.pos = 0;
353             state.status = Status::InProgress;
354         });
355     }
356 
357     /// Finishes the progress bar and leaves the current message
finish(&self)358     pub fn finish(&self) {
359         self.state.lock().unwrap().finish();
360     }
361 
362     /// Finishes the progress bar at current position and leaves the current message
finish_at_current_pos(&self)363     pub fn finish_at_current_pos(&self) {
364         self.state.lock().unwrap().finish_at_current_pos();
365     }
366 
367     /// Finishes the progress bar and sets a message
368     ///
369     /// For the message to be visible, the `{msg}` placeholder must be present in the template (see
370     /// [`ProgressStyle`]).
finish_with_message(&self, msg: impl Into<Cow<'static, str>>)371     pub fn finish_with_message(&self, msg: impl Into<Cow<'static, str>>) {
372         self.state.lock().unwrap().finish_with_message(msg);
373     }
374 
375     /// Finishes the progress bar and completely clears it
finish_and_clear(&self)376     pub fn finish_and_clear(&self) {
377         self.state.lock().unwrap().finish_and_clear();
378     }
379 
380     /// Finishes the progress bar and leaves the current message and progress
abandon(&self)381     pub fn abandon(&self) {
382         self.state.lock().unwrap().abandon();
383     }
384 
385     /// Finishes the progress bar and sets a message, and leaves the current progress
386     ///
387     /// For the message to be visible, the `{msg}` placeholder must be present in the template (see
388     /// [`ProgressStyle`]).
abandon_with_message(&self, msg: impl Into<Cow<'static, str>>)389     pub fn abandon_with_message(&self, msg: impl Into<Cow<'static, str>>) {
390         self.state.lock().unwrap().abandon_with_message(msg);
391     }
392 
393     /// Finishes the progress bar using the behavior stored in the [`ProgressStyle`]
394     ///
395     /// See [`ProgressStyle::on_finish()`].
finish_using_style(&self)396     pub fn finish_using_style(&self) {
397         self.state.lock().unwrap().finish_using_style();
398     }
399 
400     /// Sets a different draw target for the progress bar
401     ///
402     /// This can be used to draw the progress bar to stderr (this is the default):
403     ///
404     /// ```rust,no_run
405     /// # use indicatif::{ProgressBar, ProgressDrawTarget};
406     /// let pb = ProgressBar::new(100);
407     /// pb.set_draw_target(ProgressDrawTarget::stderr());
408     /// ```
409     ///
410     /// **Note:** Calling this method on a [`ProgressBar`] linked with a [`MultiProgress`] (after
411     /// running [`MultiProgress::add`]) will unlink this progress bar. If you don't want this
412     /// behavior, call [`MultiProgress::set_draw_target`] instead.
set_draw_target(&self, target: ProgressDrawTarget)413     pub fn set_draw_target(&self, target: ProgressDrawTarget) {
414         let mut state = self.state.lock().unwrap();
415         state.draw_target.disconnect();
416         state.draw_target = target;
417     }
418 
419     /// Wraps an [`Iterator`] with the progress bar
420     ///
421     /// ```rust,no_run
422     /// # use indicatif::ProgressBar;
423     /// let v = vec![1, 2, 3];
424     /// let pb = ProgressBar::new(3);
425     /// for item in pb.wrap_iter(v.iter()) {
426     ///     // ...
427     /// }
428     /// ```
wrap_iter<It: Iterator>(&self, it: It) -> ProgressBarIter<It>429     pub fn wrap_iter<It: Iterator>(&self, it: It) -> ProgressBarIter<It> {
430         it.progress_with(self.clone())
431     }
432 
433     /// Wraps an [`io::Read`] with the progress bar
434     ///
435     /// ```rust,no_run
436     /// # use std::fs::File;
437     /// # use std::io;
438     /// # use indicatif::ProgressBar;
439     /// # fn test () -> io::Result<()> {
440     /// let source = File::open("work.txt")?;
441     /// let mut target = File::create("done.txt")?;
442     /// let pb = ProgressBar::new(source.metadata()?.len());
443     /// io::copy(&mut pb.wrap_read(source), &mut target);
444     /// # Ok(())
445     /// # }
446     /// ```
wrap_read<R: io::Read>(&self, read: R) -> ProgressBarIter<R>447     pub fn wrap_read<R: io::Read>(&self, read: R) -> ProgressBarIter<R> {
448         ProgressBarIter {
449             progress: self.clone(),
450             it: read,
451         }
452     }
453 
454     /// Wraps an [`io::Write`] with the progress bar
455     ///
456     /// ```rust,no_run
457     /// # use std::fs::File;
458     /// # use std::io;
459     /// # use indicatif::ProgressBar;
460     /// # fn test () -> io::Result<()> {
461     /// let mut source = File::open("work.txt")?;
462     /// let target = File::create("done.txt")?;
463     /// let pb = ProgressBar::new(source.metadata()?.len());
464     /// io::copy(&mut source, &mut pb.wrap_write(target));
465     /// # Ok(())
466     /// # }
467     /// ```
wrap_write<W: io::Write>(&self, write: W) -> ProgressBarIter<W>468     pub fn wrap_write<W: io::Write>(&self, write: W) -> ProgressBarIter<W> {
469         ProgressBarIter {
470             progress: self.clone(),
471             it: write,
472         }
473     }
474 
update_and_draw<F: FnOnce(&mut ProgressState)>(&self, f: F)475     fn update_and_draw<F: FnOnce(&mut ProgressState)>(&self, f: F) {
476         // Delegate to the wrapped state.
477         let mut state = self.state.lock().unwrap();
478         state.update_and_draw(f);
479     }
480 
481     /// Returns the current position
position(&self) -> u64482     pub fn position(&self) -> u64 {
483         self.state.lock().unwrap().pos
484     }
485 
486     /// Returns the current length
length(&self) -> u64487     pub fn length(&self) -> u64 {
488         self.state.lock().unwrap().len
489     }
490 
491     /// Returns the current ETA
eta(&self) -> Duration492     pub fn eta(&self) -> Duration {
493         self.state.lock().unwrap().eta()
494     }
495 
496     /// Returns the current rate of progress
per_sec(&self) -> u64497     pub fn per_sec(&self) -> u64 {
498         self.state.lock().unwrap().per_sec()
499     }
500 
501     /// Returns the current expected duration
duration(&self) -> Duration502     pub fn duration(&self) -> Duration {
503         self.state.lock().unwrap().duration()
504     }
505 
506     /// Returns the current elapsed time
elapsed(&self) -> Duration507     pub fn elapsed(&self) -> Duration {
508         self.state.lock().unwrap().started.elapsed()
509     }
510 }
511 
512 /// Manages multiple progress bars from different threads
513 pub struct MultiProgress {
514     state: Arc<RwLock<MultiProgressState>>,
515     joining: AtomicBool,
516     tx: Sender<(usize, ProgressDrawState)>,
517     rx: Receiver<(usize, ProgressDrawState)>,
518 }
519 
520 impl fmt::Debug for MultiProgress {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result521     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522         f.debug_struct("MultiProgress").finish()
523     }
524 }
525 
526 unsafe impl Sync for MultiProgress {}
527 
528 impl Default for MultiProgress {
default() -> MultiProgress529     fn default() -> MultiProgress {
530         MultiProgress::with_draw_target(ProgressDrawTarget::stderr())
531     }
532 }
533 
534 impl MultiProgress {
535     /// Creates a new multi progress object.
536     ///
537     /// Progress bars added to this object by default draw directly to stderr, and refresh
538     /// a maximum of 15 times a second. To change the refresh rate set the draw target to
539     /// one with a different refresh rate.
new() -> MultiProgress540     pub fn new() -> MultiProgress {
541         MultiProgress::default()
542     }
543 
544     /// Creates a new multi progress object with the given draw target.
with_draw_target(draw_target: ProgressDrawTarget) -> MultiProgress545     pub fn with_draw_target(draw_target: ProgressDrawTarget) -> MultiProgress {
546         let (tx, rx) = channel();
547         MultiProgress {
548             state: Arc::new(RwLock::new(MultiProgressState {
549                 objects: Vec::new(),
550                 free_set: Vec::new(),
551                 ordering: vec![],
552                 draw_target,
553                 move_cursor: false,
554             })),
555             joining: AtomicBool::new(false),
556             tx,
557             rx,
558         }
559     }
560 
561     /// Sets a different draw target for the multiprogress bar.
set_draw_target(&self, target: ProgressDrawTarget)562     pub fn set_draw_target(&self, target: ProgressDrawTarget) {
563         let mut state = self.state.write().unwrap();
564         state.draw_target.disconnect();
565         state.draw_target = target;
566     }
567 
568     /// Set whether we should try to move the cursor when possible instead of clearing lines.
569     ///
570     /// This can reduce flickering, but do not enable it if you intend to change the number of
571     /// progress bars.
set_move_cursor(&self, move_cursor: bool)572     pub fn set_move_cursor(&self, move_cursor: bool) {
573         self.state.write().unwrap().move_cursor = move_cursor;
574     }
575 
576     /// Adds a progress bar.
577     ///
578     /// The progress bar added will have the draw target changed to a
579     /// remote draw target that is intercepted by the multi progress
580     /// object overriding custom `ProgressDrawTarget` settings.
add(&self, pb: ProgressBar) -> ProgressBar581     pub fn add(&self, pb: ProgressBar) -> ProgressBar {
582         self.push(None, pb)
583     }
584 
585     /// Inserts a progress bar.
586     ///
587     /// The progress bar inserted at position `index` will have the draw
588     /// target changed to a remote draw target that is intercepted by the
589     /// multi progress object overriding custom `ProgressDrawTarget` settings.
590     ///
591     /// If `index >= MultiProgressState::objects.len()`, the progress bar
592     /// is added to the end of the list.
insert(&self, index: usize, pb: ProgressBar) -> ProgressBar593     pub fn insert(&self, index: usize, pb: ProgressBar) -> ProgressBar {
594         self.push(Some(index), pb)
595     }
596 
push(&self, pos: Option<usize>, pb: ProgressBar) -> ProgressBar597     fn push(&self, pos: Option<usize>, pb: ProgressBar) -> ProgressBar {
598         let new = MultiObject {
599             done: false,
600             draw_state: None,
601         };
602 
603         let mut state = self.state.write().unwrap();
604         let idx = match state.free_set.pop() {
605             Some(idx) => {
606                 state.objects[idx] = Some(new);
607                 idx
608             }
609             None => {
610                 state.objects.push(Some(new));
611                 state.objects.len() - 1
612             }
613         };
614 
615         match pos {
616             Some(pos) if pos < state.ordering.len() => state.ordering.insert(pos, idx),
617             _ => state.ordering.push(idx),
618         }
619 
620         pb.set_draw_target(ProgressDrawTarget {
621             kind: ProgressDrawTargetKind::Remote {
622                 state: self.state.clone(),
623                 idx,
624                 chan: Mutex::new(self.tx.clone()),
625             },
626         });
627         pb
628     }
629 
630     /// Removes a progress bar.
631     ///
632     /// The progress bar is removed only if it was previously inserted or added
633     /// by the methods `MultiProgress::insert` or `MultiProgress::add`.
634     /// If the passed progress bar does not satisfy the condition above,
635     /// the `remove` method does nothing.
remove(&self, pb: &ProgressBar)636     pub fn remove(&self, pb: &ProgressBar) {
637         let idx = match &pb.state.lock().unwrap().draw_target.kind {
638             ProgressDrawTargetKind::Remote { state, idx, .. } => {
639                 // Check that this progress bar is owned by the current MultiProgress.
640                 assert!(Arc::ptr_eq(&self.state, state));
641                 *idx
642             }
643             _ => return,
644         };
645 
646         self.state.write().unwrap().remove_idx(idx);
647     }
648 
649     /// Waits for all progress bars to report that they are finished.
650     ///
651     /// You need to call this as this will request the draw instructions
652     /// from the remote progress bars.  Not calling this will deadlock
653     /// your program.
join(&self) -> io::Result<()>654     pub fn join(&self) -> io::Result<()> {
655         self.join_impl(false)
656     }
657 
658     /// Works like `join` but clears the progress bar in the end.
join_and_clear(&self) -> io::Result<()>659     pub fn join_and_clear(&self) -> io::Result<()> {
660         self.join_impl(true)
661     }
662 
join_impl(&self, clear: bool) -> io::Result<()>663     fn join_impl(&self, clear: bool) -> io::Result<()> {
664         if self.joining.load(Ordering::Acquire) {
665             panic!("Already joining!");
666         }
667         self.joining.store(true, Ordering::Release);
668 
669         let move_cursor = self.state.read().unwrap().move_cursor;
670         // Max amount of grouped together updates at once. This is meant
671         // to ensure there isn't a situation where continuous updates prevent
672         // any actual draws happening.
673         const MAX_GROUP_SIZE: usize = 32;
674         let mut recv_peek = None;
675         let mut grouped = 0usize;
676         let mut orphan_lines: Vec<String> = Vec::new();
677         let mut force_draw = false;
678         while !self.state.read().unwrap().is_done() {
679             let (idx, draw_state) = if let Some(peeked) = recv_peek.take() {
680                 peeked
681             } else {
682                 self.rx.recv().unwrap()
683             };
684             force_draw |= draw_state.finished || draw_state.force_draw;
685 
686             let mut state = self.state.write().unwrap();
687             if draw_state.finished {
688                 if let Some(ref mut obj) = &mut state.objects[idx] {
689                     obj.done = true;
690                 }
691                 if draw_state.lines.is_empty() {
692                     // `finish_and_clear` was called
693                     state.remove_idx(idx);
694                 }
695             }
696 
697             // Split orphan lines out of the draw state, if any
698             let lines = if draw_state.orphan_lines > 0 {
699                 let split = draw_state.lines.split_at(draw_state.orphan_lines);
700                 orphan_lines.extend_from_slice(split.0);
701                 split.1.to_vec()
702             } else {
703                 draw_state.lines
704             };
705 
706             let draw_state = ProgressDrawState {
707                 lines,
708                 orphan_lines: 0,
709                 ..draw_state
710             };
711 
712             if let Some(ref mut obj) = &mut state.objects[idx] {
713                 obj.draw_state = Some(draw_state);
714             }
715 
716             // the rest from here is only drawing, we can skip it.
717             if state.draw_target.is_hidden() {
718                 continue;
719             }
720 
721             debug_assert!(recv_peek.is_none());
722             if grouped >= MAX_GROUP_SIZE {
723                 // Can't group any more draw calls, proceed to just draw
724                 grouped = 0;
725             } else if let Ok(state) = self.rx.try_recv() {
726                 // Only group draw calls if there is another draw already queued
727                 recv_peek = Some(state);
728                 grouped += 1;
729                 continue;
730             } else {
731                 // No more draws queued, proceed to just draw
732                 grouped = 0;
733             }
734 
735             let mut lines = vec![];
736 
737             // Make orphaned lines appear at the top, so they can be properly
738             // forgotten.
739             let orphan_lines_count = orphan_lines.len();
740             lines.append(&mut orphan_lines);
741 
742             for index in state.ordering.iter() {
743                 if let Some(obj) = &state.objects[*index] {
744                     if let Some(ref draw_state) = obj.draw_state {
745                         lines.extend_from_slice(&draw_state.lines[..]);
746                     }
747                 }
748             }
749 
750             let finished = state.is_done();
751             state.draw_target.apply_draw_state(ProgressDrawState {
752                 lines,
753                 orphan_lines: orphan_lines_count,
754                 force_draw: force_draw || orphan_lines_count > 0,
755                 move_cursor,
756                 finished,
757             })?;
758 
759             force_draw = false;
760         }
761 
762         if clear {
763             let mut state = self.state.write().unwrap();
764             state.draw_target.apply_draw_state(ProgressDrawState {
765                 lines: vec![],
766                 orphan_lines: 0,
767                 finished: true,
768                 force_draw: true,
769                 move_cursor,
770             })?;
771         }
772 
773         self.joining.store(false, Ordering::Release);
774 
775         Ok(())
776     }
777 }
778 
779 /// A weak reference to a `ProgressBar`.
780 ///
781 /// Useful for creating custom steady tick implementations
782 #[derive(Clone)]
783 pub struct WeakProgressBar {
784     state: Weak<Mutex<ProgressState>>,
785 }
786 
787 impl WeakProgressBar {
788     /// Attempts to upgrade the Weak pointer to a [`ProgressBar`], delaying dropping of the inner
789     /// value if successful. Returns `None` if the inner value has since been dropped.
790     ///
791     /// [`ProgressBar`]: struct.ProgressBar.html
upgrade(&self) -> Option<ProgressBar>792     pub fn upgrade(&self) -> Option<ProgressBar> {
793         self.state.upgrade().map(|state| ProgressBar { state })
794     }
795 }
796 
797 #[cfg(test)]
798 mod tests {
799     use super::*;
800 
801     #[allow(clippy::float_cmp)]
802     #[test]
test_pbar_zero()803     fn test_pbar_zero() {
804         let pb = ProgressBar::new(0);
805         assert_eq!(pb.state.lock().unwrap().fraction(), 1.0);
806     }
807 
808     #[allow(clippy::float_cmp)]
809     #[test]
test_pbar_maxu64()810     fn test_pbar_maxu64() {
811         let pb = ProgressBar::new(!0);
812         assert_eq!(pb.state.lock().unwrap().fraction(), 0.0);
813     }
814 
815     #[test]
test_pbar_overflow()816     fn test_pbar_overflow() {
817         let pb = ProgressBar::new(1);
818         pb.set_draw_target(ProgressDrawTarget::hidden());
819         pb.inc(2);
820         pb.finish();
821     }
822 
823     #[test]
test_get_position()824     fn test_get_position() {
825         let pb = ProgressBar::new(1);
826         pb.set_draw_target(ProgressDrawTarget::hidden());
827         pb.inc(2);
828         let pos = pb.position();
829         assert_eq!(pos, 2);
830     }
831 
832     #[test]
test_weak_pb()833     fn test_weak_pb() {
834         let pb = ProgressBar::new(0);
835         let weak = pb.downgrade();
836         assert!(weak.upgrade().is_some());
837         ::std::mem::drop(pb);
838         assert!(weak.upgrade().is_none());
839     }
840 
841     #[test]
test_draw_delta_deadlock()842     fn test_draw_delta_deadlock() {
843         // see issue #187
844         let mpb = MultiProgress::new();
845         let pb = mpb.add(ProgressBar::new(1));
846         pb.set_draw_delta(2);
847         drop(pb);
848         mpb.join().unwrap();
849     }
850 
851     #[test]
test_abandon_deadlock()852     fn test_abandon_deadlock() {
853         let mpb = MultiProgress::new();
854         let pb = mpb.add(ProgressBar::new(1));
855         pb.set_draw_delta(2);
856         pb.abandon();
857         drop(pb);
858         mpb.join().unwrap();
859     }
860 
861     #[test]
late_pb_drop()862     fn late_pb_drop() {
863         let pb = ProgressBar::new(10);
864         let mpb = MultiProgress::new();
865         // This clone call is required to trigger a now fixed bug.
866         // See <https://github.com/mitsuhiko/indicatif/pull/141> for context
867         #[allow(clippy::redundant_clone)]
868         mpb.add(pb.clone());
869     }
870 
871     #[test]
it_can_wrap_a_reader()872     fn it_can_wrap_a_reader() {
873         let bytes = &b"I am an implementation of io::Read"[..];
874         let pb = ProgressBar::new(bytes.len() as u64);
875         let mut reader = pb.wrap_read(bytes);
876         let mut writer = Vec::new();
877         io::copy(&mut reader, &mut writer).unwrap();
878         assert_eq!(writer, bytes);
879     }
880 
881     #[test]
it_can_wrap_a_writer()882     fn it_can_wrap_a_writer() {
883         let bytes = b"implementation of io::Read";
884         let mut reader = &bytes[..];
885         let pb = ProgressBar::new(bytes.len() as u64);
886         let writer = Vec::new();
887         let mut writer = pb.wrap_write(writer);
888         io::copy(&mut reader, &mut writer).unwrap();
889         assert_eq!(writer.it, bytes);
890     }
891 
892     #[test]
progress_bar_sync_send()893     fn progress_bar_sync_send() {
894         let _: Box<dyn Sync> = Box::new(ProgressBar::new(1));
895         let _: Box<dyn Send> = Box::new(ProgressBar::new(1));
896         let _: Box<dyn Sync> = Box::new(MultiProgress::new());
897         let _: Box<dyn Send> = Box::new(MultiProgress::new());
898     }
899 
900     #[test]
multi_progress_modifications()901     fn multi_progress_modifications() {
902         let mp = MultiProgress::new();
903         let p0 = mp.add(ProgressBar::new(1));
904         let p1 = mp.add(ProgressBar::new(1));
905         let p2 = mp.add(ProgressBar::new(1));
906         let p3 = mp.add(ProgressBar::new(1));
907         mp.remove(&p2);
908         mp.remove(&p1);
909         let p4 = mp.insert(1, ProgressBar::new(1));
910 
911         let state = mp.state.read().unwrap();
912         // the removed place for p1 is reused
913         assert_eq!(state.objects.len(), 4);
914         assert_eq!(state.objects.iter().filter(|o| o.is_some()).count(), 3);
915 
916         // free_set may contain 1 or 2
917         match state.free_set.last() {
918             Some(1) => {
919                 assert_eq!(state.ordering, vec![0, 2, 3]);
920                 assert_eq!(extract_index(&p4), 2);
921             }
922             Some(2) => {
923                 assert_eq!(state.ordering, vec![0, 1, 3]);
924                 assert_eq!(extract_index(&p4), 1);
925             }
926             _ => unreachable!(),
927         }
928 
929         assert_eq!(extract_index(&p0), 0);
930         assert_eq!(extract_index(&p1), 1);
931         assert_eq!(extract_index(&p2), 2);
932         assert_eq!(extract_index(&p3), 3);
933     }
934 
935     #[test]
multi_progress_multiple_remove()936     fn multi_progress_multiple_remove() {
937         let mp = MultiProgress::new();
938         let p0 = mp.add(ProgressBar::new(1));
939         let p1 = mp.add(ProgressBar::new(1));
940         // double remove beyond the first one have no effect
941         mp.remove(&p0);
942         mp.remove(&p0);
943         mp.remove(&p0);
944 
945         let state = mp.state.read().unwrap();
946         // the removed place for p1 is reused
947         assert_eq!(state.objects.len(), 2);
948         assert_eq!(state.objects.iter().filter(|obj| obj.is_some()).count(), 1);
949         assert_eq!(state.free_set.last(), Some(&0));
950 
951         assert_eq!(state.ordering, vec![1]);
952         assert_eq!(extract_index(&p0), 0);
953         assert_eq!(extract_index(&p1), 1);
954     }
955 
extract_index(pb: &ProgressBar) -> usize956     fn extract_index(pb: &ProgressBar) -> usize {
957         match pb.state.lock().unwrap().draw_target.kind {
958             ProgressDrawTargetKind::Remote { idx, .. } => idx,
959             _ => unreachable!(),
960         }
961     }
962 
963     #[test]
multi_progress_hidden()964     fn multi_progress_hidden() {
965         let mpb = MultiProgress::with_draw_target(ProgressDrawTarget::hidden());
966         let pb = mpb.add(ProgressBar::new(123));
967         pb.finish();
968         mpb.join().unwrap();
969     }
970 }
971