Skip to main content

pistonite_cu/cli/progress/
builder.rs

1use std::sync::Arc;
2
3use crate::cli::progress::{Estimater, ProgressBar, State, StateImmut};
4
5/// Builder for a progress bar
6#[derive(Debug, Clone)] // Clone sometimes needed to build by ref.. without unsafe
7pub struct ProgressBarBuilder {
8    /// The message prefix for the progress bar
9    message: String,
10    /// Total steps (None = unbounded, 0 = not known yet)
11    total: Option<u64>,
12    /// The progress bar is for displaying bytes
13    total_is_in_bytes: bool,
14    /// If the bar should be kept after it's done
15    keep: Option<bool>,
16    /// If ETA should be visible (only effective if total is finite)
17    show_eta: bool,
18    /// If percentage should be visible (only effective if total is finite)
19    show_percentage: bool,
20    /// Message to display after done, instead of the default
21    done_message: Option<String>,
22    /// Message to display if the bar is interrupted
23    interrupted_message: Option<String>,
24    /// Maximum number of children to display at a time
25    max_display_children: usize,
26    /// Optional parent of the bar
27    parent: Option<Arc<ProgressBar>>,
28}
29
30impl ProgressBarBuilder {
31    /// Start building a progress bar. Note [`cu::progress`](fn@crate::progress) is the canonical shorthand
32    pub fn new(message: String) -> Self {
33        Self {
34            message,
35            total: None,
36            total_is_in_bytes: false,
37            keep: None,
38            show_eta: true,
39            show_percentage: true,
40            done_message: None,
41            interrupted_message: None,
42            max_display_children: usize::MAX / 2,
43            parent: None,
44        }
45    }
46    /// Set the total steps. `0` means total is unknown, which can be set
47    /// at a later point.
48    ///
49    /// By default, the progress bar is "unbounded", meaning there is no
50    /// individual steps
51    ///
52    /// ```rust
53    /// # use pistonite_cu as cu;
54    /// cu::progress("doing something").total(10);
55    /// ```
56    #[inline(always)]
57    pub fn total(mut self, total: usize) -> Self {
58        self.total = Some(total as u64);
59        self
60    }
61
62    /// Set the total as a `u64` on platforms where `usize` is less than 64 bits
63    #[cfg(not(target_pointer_width = "64"))]
64    pub fn total_u64(mut self, total: u64) -> Self {
65        self.total = Some(total);
66        self
67    }
68
69    /// Set the total bytes and set the progress to be displayed using byte units (SI).
70    /// `0` means total is unknown, which can be set at a later point.
71    ///
72    /// ```rust
73    /// # use pistonite_cu as cu;
74    /// cu::progress("doing something").total_bytes(1000000);
75    /// ```
76    #[inline(always)]
77    pub fn total_bytes(mut self, total: u64) -> Self {
78        self.total = Some(total);
79        self.total_is_in_bytes = true;
80        self
81    }
82
83    /// Set if the progress bar should be kept in the output
84    /// after it's done.
85    ///
86    /// Default is `true` for root progress bars and `false`
87    /// for child progress bars
88    ///
89    /// ```rust
90    /// # use pistonite_cu as cu;
91    /// cu::progress("doing something").keep(false);
92    /// ```
93    #[inline(always)]
94    pub fn keep(mut self, keep: bool) -> Self {
95        self.keep = Some(keep);
96        self
97    }
98
99    /// Set if ETA (estimated time) should be displayed.
100    /// Only effective if total is not zero (i.e. not unbounded).
101    /// Default is `true`
102    ///
103    /// ```rust
104    /// # use pistonite_cu as cu;
105    /// cu::progress("doing something").total(10).eta(false);
106    /// ```
107    #[inline(always)]
108    pub fn eta(mut self, show: bool) -> Self {
109        self.show_eta = show;
110        self
111    }
112
113    /// Set if percentage should be displayed.
114    /// Only effective if total is not zero (i.e. not unbounded).
115    /// Default is `true`
116    ///
117    /// ```rust
118    /// # use pistonite_cu as cu;
119    /// cu::progress("doing something").total(10).percentage(false);
120    /// ```
121    #[inline(always)]
122    pub fn percentage(mut self, show: bool) -> Self {
123        self.show_percentage = show;
124        self
125    }
126
127    /// Set a message to be displayed when the progress is done.
128    /// Requires `keep(true)` - which is the default, but
129    /// `when_done` will not automatically turn it on for you.
130    ///
131    /// Default is the message of the progress bar followed by `done`.
132    ///
133    /// ```rust
134    /// # use pistonite_cu as cu;
135    /// cu::progress("doing something").when_done("something is done!");
136    /// ```
137    #[inline(always)]
138    pub fn when_done(mut self, message: impl Into<String>) -> Self {
139        self.done_message = Some(message.into());
140        self
141    }
142
143    /// Set a message to be displayed when the progress is interrupted.
144    ///
145    /// Default is the message of the progress bar followed by `interrupted`.
146    ///
147    /// ```rust
148    /// # use pistonite_cu as cu;
149    /// cu::progress("doing something").when_interrupt("something is interrupted!");
150    /// ```
151    #[inline(always)]
152    pub fn when_interrupt(mut self, message: impl Into<String>) -> Self {
153        self.interrupted_message = Some(message.into());
154        self
155    }
156
157    /// Set the max number of children to display at a time.
158    /// Default is unbounded.
159    ///
160    /// ```rust
161    /// # use pistonite_cu as cu;
162    /// cu::progress("doing something").max_display_children(30);
163    /// ```
164    pub fn max_display_children(mut self, num: usize) -> Self {
165        self.max_display_children = num;
166        self
167    }
168
169    /// Set the parent progress bar.
170    ///
171    /// If the parent is known to be `Some`, use `parent.child(...)` instead
172    pub fn parent(mut self, parent: Option<Arc<ProgressBar>>) -> Self {
173        self.parent = parent;
174        self
175    }
176
177    /// Build and start displaying the bar in the console
178    pub fn spawn(self) -> Arc<ProgressBar> {
179        let keep = self.keep.unwrap_or(self.parent.is_none());
180        let done_message = if keep {
181            match self.done_message {
182                None => {
183                    if self.message.is_empty() {
184                        Some("done".to_string())
185                    } else {
186                        Some(format!("{}: done", self.message))
187                    }
188                }
189                Some(x) => Some(x),
190            }
191        } else {
192            None
193        };
194        let state_immut = StateImmut {
195            id: crate::next_atomic_usize(),
196            parent: self.parent.as_ref().map(Arc::clone),
197            prefix: self.message,
198            show_percentage: self.show_percentage,
199            unbounded: self.total.is_none(),
200            display_bytes: self.total_is_in_bytes,
201            max_display_children: self.max_display_children,
202        };
203        let eta = self.show_eta.then(Estimater::new);
204        let state = State::new(
205            self.total.unwrap_or(0),
206            eta,
207            done_message,
208            self.interrupted_message,
209        );
210
211        ProgressBar::spawn(state_immut, state, self.parent)
212    }
213}