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}