Mercurial > public > mercurial-scm > hg
comparison rust/hg-core/src/progress.rs @ 52038:92e23ba257d1
rust-hg-cpython: add an `HgProgressBar` util
This will be the entry point for all progress bars from a Python context
in upcoming patches. Like the `Progress` trait, this is subject to change
once we have more use cases, but this is good enough for now.
author | Rapha?l Gom?s <rgomes@octobus.net> |
---|---|
date | Mon, 30 Sep 2024 16:04:51 +0200 |
parents | 3ae7c43ad8aa |
children | a876ab6c3fd5 |
comparison
equal
deleted
inserted
replaced
52037:3ae7c43ad8aa | 52038:92e23ba257d1 |
---|---|
1 //! Progress-bar related things | 1 //! Progress-bar related things |
2 | |
3 use std::{ | |
4 sync::atomic::{AtomicBool, Ordering}, | |
5 time::Duration, | |
6 }; | |
7 | |
8 use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle}; | |
2 | 9 |
3 /// A generic determinate progress bar trait | 10 /// A generic determinate progress bar trait |
4 pub trait Progress: Send + Sync + 'static { | 11 pub trait Progress: Send + Sync + 'static { |
5 /// Set the current position and optionally the total | 12 /// Set the current position and optionally the total |
6 fn update(&self, pos: u64, total: Option<u64>); | 13 fn update(&self, pos: u64, total: Option<u64>); |
7 /// Increment the current position and optionally the total | 14 /// Increment the current position and optionally the total |
8 fn increment(&self, step: u64, total: Option<u64>); | 15 fn increment(&self, step: u64, total: Option<u64>); |
9 /// Declare that progress is over and the progress bar should be deleted | 16 /// Declare that progress is over and the progress bar should be deleted |
10 fn complete(self); | 17 fn complete(self); |
11 } | 18 } |
19 | |
20 const PROGRESS_DELAY: Duration = Duration::from_secs(1); | |
21 | |
22 /// A generic (determinate) progress bar. Stays hidden until [`PROGRESS_DELAY`] | |
23 /// to prevent flickering a progress bar for super fast operations. | |
24 pub struct HgProgressBar { | |
25 progress: ProgressBar, | |
26 has_been_shown: AtomicBool, | |
27 } | |
28 | |
29 impl HgProgressBar { | |
30 // TODO pass config to check progress.disable/assume-tty/delay/etc. | |
31 /// Return a new progress bar with `topic` as the prefix. | |
32 /// The progress and total are both set to 0, and it is hidden until the | |
33 /// next call to `update` given that more than a second has elapsed. | |
34 pub fn new(topic: &str) -> Self { | |
35 let template = | |
36 format!("{} {{wide_bar}} {{pos}}/{{len}} {{eta}} ", topic); | |
37 let style = ProgressStyle::with_template(&template).unwrap(); | |
38 let progress_bar = ProgressBar::new(0).with_style(style); | |
39 // Hide the progress bar and only show it if we've elapsed more | |
40 // than a second | |
41 progress_bar.set_draw_target(ProgressDrawTarget::hidden()); | |
42 Self { | |
43 progress: progress_bar, | |
44 has_been_shown: false.into(), | |
45 } | |
46 } | |
47 | |
48 /// Called whenever the progress changes to determine whether to start | |
49 /// showing the progress bar | |
50 fn maybe_show(&self) { | |
51 if self.progress.is_hidden() | |
52 && self.progress.elapsed() > PROGRESS_DELAY | |
53 { | |
54 // Catch a race condition whereby we check if it's hidden, then | |
55 // set the draw target from another thread, then do it again from | |
56 // this thread, which results in multiple progress bar lines being | |
57 // left drawn. | |
58 let has_been_shown = | |
59 self.has_been_shown.fetch_or(true, Ordering::Relaxed); | |
60 if !has_been_shown { | |
61 // Here we are certain that we're the only thread that has | |
62 // set `has_been_shown` and we can change the draw target | |
63 self.progress.set_draw_target(ProgressDrawTarget::stderr()); | |
64 self.progress.tick(); | |
65 } | |
66 } | |
67 } | |
68 } | |
69 | |
70 impl Progress for HgProgressBar { | |
71 fn update(&self, pos: u64, total: Option<u64>) { | |
72 self.progress.update(|state| { | |
73 state.set_pos(pos); | |
74 if let Some(t) = total { | |
75 state.set_len(t) | |
76 } | |
77 }); | |
78 self.maybe_show(); | |
79 } | |
80 | |
81 fn increment(&self, step: u64, total: Option<u64>) { | |
82 self.progress.inc(step); | |
83 if let Some(t) = total { | |
84 self.progress.set_length(t) | |
85 } | |
86 self.maybe_show(); | |
87 } | |
88 | |
89 fn complete(self) { | |
90 self.progress.finish_and_clear(); | |
91 } | |
92 } |