ml_progress/
macros.rs

1// ======================================================================
2// MACROS - PUBLIC
3
4/// Creates [`Progress`] with default configuration.
5///
6/// # Usage
7///
8/// This macro takes total and/or items as arguments
9/// and returns `Result<`[`Progress`]`,`[`Error`]`>` using default configuration.
10///
11/// - If total is `Some`, it's given as expression which returns `impl TryInto<u64>`.
12/// - If total is `None`, it's given as `None` or empty.
13/// - If items are given, they must be preceded by `;`
14///   after which actual items are given using special syntax
15///   documented at [items](crate#items).
16/// - Default items: `bar_fill " " pos "/" total " (" eta ")"`
17///
18/// ```ignore
19/// progress!(EXPR)          // total is `Some(EXPR)`, default items
20/// progress!(EXPR ; ITEMS)  // total is `Some(EXPR)`, given items
21/// progress!(None ; ITEMS)  // total is `None`, given items
22/// progress!(     ; ITEMS)  // - same
23/// ```
24///
25/// # Examples
26///
27/// See crate index for [usage](crate#usage) and [examples](crate#examples)
28/// in larger context. Following examples are only about this macro.
29///
30/// ```rust
31/// use ml_progress::progress;
32///
33/// // Total is `Some(10)`, default items.
34/// let progress = progress!(10)?;
35///
36/// // Total is `Some(10)`, given items.
37/// let progress = progress!(10; pos "/" total " " bar_fill)?;
38///
39/// // Total is `None`, given items.
40/// let progress = progress!(None; pos "/" total " " bar_fill)?;
41/// let progress = progress!(    ; pos "/" total " " bar_fill)?;
42///
43/// # Ok::<(), ml_progress::Error>(())
44/// ```
45///
46/// [`Error`]: crate::Error
47/// [`Progress`]: crate::Progress
48#[macro_export]
49macro_rules! progress {
50    ( $(None)? ; $($item:tt)+ ) => {
51        $crate::ProgressBuilder::new($crate::items!($($item)+)).build()
52    };
53
54    ( $total:expr $( ; $($item:tt)* )? ) => {
55        $crate::ProgressBuilder::new($crate::items!($($($item)*)?))
56            .total(Some($total))
57            .build()
58    };
59}
60
61/// Creates [`ProgressBuilder`] to configure [`Progress`].
62///
63/// # Usage
64///
65/// This macro takes optional items as arguments and returns [`ProgressBuilder`].
66///
67/// - Items are given using special syntax documented at [items](crate#items).
68/// - Default items: `bar_fill " " pos "/" total " (" eta ")"`
69///
70/// ```ignore
71/// progress_builder!()       // default items
72/// progress_builder!(ITEMS)  // given items
73/// ```
74///
75/// # Examples
76///
77/// See crate index for [usage](crate#usage) and [examples](crate#examples)
78/// in larger context. Following examples are only about this macro.
79///
80/// ```rust
81/// use ml_progress::progress_builder;
82///
83/// // Default items.
84/// let builder = progress_builder!();
85///
86/// // Given items.
87/// let builder = progress_builder!(pos "/" total " " bar_fill);
88///
89/// ```
90///
91/// [`Progress`]: crate::Progress
92/// [`ProgressBuilder`]: crate::ProgressBuilder
93#[macro_export]
94macro_rules! progress_builder {
95    ( $($item:tt)* ) => {
96        $crate::ProgressBuilder::new($crate::items!($($item)*))
97    };
98}
99
100/// _Internal_ Creates `Vec<`[`Item`]`>`.
101///
102/// This is used internally by [`progress`] and [`progress_builder`] macros.
103///
104/// [`Item`]: crate::internal::Item
105#[macro_export]
106macro_rules! items {
107    ( $($item:tt)* ) => {
108        vec![ $( $crate::item!($item) ),* ]
109    };
110}
111
112/// _Internal_ Creates one [`Item`].
113///
114/// This is used internally by [`items`] macro.
115///
116/// [`Item`]: crate::internal::Item
117#[macro_export]
118macro_rules! item {
119    // ============================================================
120    // BAR
121
122    ( bar_fill ) => {
123        $crate::internal::Item::Fill($crate::internal::FillItem::Bar)
124    };
125
126    // ============================================================
127    // ETA
128
129    (  eta                  ) => { $crate::item!(( eta "{}{}"  "" )) };
130    (( eta $format:literal )) => { $crate::item!(( eta $format "" )) };
131
132    (( eta $format:literal $none:literal )) => {
133        $crate::internal::Item::Fn(Box::new(|s| {
134            if let Some(eta) = s.eta() {
135                let (amount, unit) = $crate::duration_approx(eta);
136                format!(
137                    $format,
138                    $crate::internal::FormatInteger::new(
139                        amount,
140                        s.thousands_separator()
141                    ),
142                    unit,
143                )
144            } else {
145                $none.to_string()
146            }
147        }))
148    };
149
150    // ============================================================
151    // ETA HMS
152
153    ( eta_hms ) => {
154        $crate::internal::Item::Fn(Box::new(|s| {
155            if let Some(eta) = s.eta() {
156                let (h,m,s) = $crate::duration_hms(eta);
157                if h > 0 {
158                    format!("{}:{:02}:{:02}", h, m, s)
159                } else {
160                    format!("{}:{:02}", m, s)
161                }
162            } else {
163                "".to_string()
164            }
165        }))
166    };
167
168    // ============================================================
169    // MESSAGE
170
171    ( message_fill ) => {
172        $crate::internal::Item::Fill($crate::internal::FillItem::Message)
173    };
174
175    // ============================================================
176    // PERCENT
177
178    (  percent                  ) => { $crate::item!(( percent "{:3.0}%" "" )) };
179    (( percent $format:literal )) => { $crate::item!(( percent $format   "" )) };
180
181    (( percent $format:literal $none:literal )) => {
182        $crate::internal::Item::Fn(Box::new(|s| {
183            if let Some(percent) = s.percent() {
184                format!($format, $crate::internal::FormatFloat::new(percent, false))
185            } else {
186                $none.to_string()
187            }
188        }))
189    };
190
191    // ============================================================
192    // POS
193
194    ( pos       ) => { $crate::item!(( pos "{}"   )) };
195    ( pos_group ) => { $crate::item!(( pos "{:#}" )) };
196
197    (( pos $format:literal )) => {
198        $crate::internal::Item::Fn(Box::new(|s| {
199            format!(
200                $format,
201                $crate::internal::FormatInteger::new(s.pos(), s.thousands_separator())
202            )
203        }))
204    };
205
206    // ============================================================
207    // POS_BIN
208
209    ( pos_bin ) => { $crate::item!(( pos_bin "{:#} {}" )) };
210
211    (( pos_bin $format:literal )) => {
212        $crate::internal::Item::Fn(Box::new(|s| {
213            let (amount, prefix) = $crate::binary_prefix(s.pos() as f64);
214            format!(
215                $format,
216                $crate::internal::FormatFloat::new(amount, prefix == ""),
217                $crate::internal::FormatPrefix::new(prefix),
218            )
219        }))
220    };
221
222    // ============================================================
223    // POS_DEC
224
225    ( pos_dec ) => { $crate::item!(( pos_dec "{:#} {}" )) };
226
227    (( pos_dec $format:literal )) => {
228        $crate::internal::Item::Fn(Box::new(|s| {
229            let (amount, prefix) = $crate::decimal_prefix(s.pos() as f64);
230            format!(
231                $format,
232                $crate::internal::FormatFloat::new(amount, prefix == ""),
233                $crate::internal::FormatPrefix::new(prefix),
234            )
235        }))
236    };
237
238    // ============================================================
239    // SPEED
240
241    (  speed                  ) => { $crate::item!(( speed "{:#}"  "" )) };
242    (( speed $format:literal )) => { $crate::item!(( speed $format "" )) };
243
244    (( speed $format:literal $none:literal )) => {
245        $crate::internal::Item::Fn(Box::new(|s| {
246            if let Some(speed) = s.speed() {
247                format!($format, $crate::internal::FormatFloat::new(speed, false))
248            } else {
249                $none.to_string()
250            }
251        }))
252    };
253
254    // ============================================================
255    // SPEED_GROUP / SPEED_INT
256
257    (  speed_int                  ) => { $crate::item!(( speed_int "{}"    "" )) };
258    (  speed_group                ) => { $crate::item!(( speed_int "{:#}"  "" )) };
259    (( speed_int $format:literal )) => { $crate::item!(( speed_int $format "" )) };
260
261    (( speed_int $format:literal $none:literal )) => {
262        $crate::internal::Item::Fn(Box::new(|s| {
263            if let Some(speed) = s.speed() {
264                format!(
265                    $format,
266                    $crate::internal::FormatInteger::new(
267                        speed.round() as u64,
268                        s.thousands_separator(),
269                    ),
270                )
271            } else {
272                $none.to_string()
273            }
274        }))
275    };
276
277    // ============================================================
278    // SPEED_BIN
279
280    (  speed_bin                  ) => { $crate::item!(( speed_bin "{:#} {}" "" )) };
281    (( speed_bin $format:literal )) => { $crate::item!(( speed_bin $format   "" )) };
282
283    (( speed_bin $format:literal $none:literal )) => {
284        $crate::internal::Item::Fn(Box::new(|s| {
285            if let Some(speed) = s.speed() {
286                let (amount, prefix) = $crate::binary_prefix(speed);
287                format!(
288                    $format,
289                    $crate::internal::FormatFloat::new(amount, false),
290                    $crate::internal::FormatPrefix::new(prefix),
291                )
292            } else {
293                $none.to_string()
294            }
295        }))
296    };
297
298    // ============================================================
299    // SPEED_DEC
300
301    (  speed_dec                  ) => { $crate::item!(( speed_dec "{:#} {}" "" )) };
302    (( speed_dec $format:literal )) => { $crate::item!(( speed_dec $format   "" )) };
303
304    (( speed_dec $format:literal $none:literal )) => {
305        $crate::internal::Item::Fn(Box::new(|s| {
306            if let Some(speed) = s.speed() {
307                let (amount, prefix) = $crate::decimal_prefix(speed);
308                format!(
309                    $format,
310                    $crate::internal::FormatFloat::new(amount, false),
311                    $crate::internal::FormatPrefix::new(prefix),
312                )
313            } else {
314                $none.to_string()
315            }
316        }))
317    };
318
319    // ============================================================
320    // TOTAL
321
322    (  total                  ) => { $crate::item!(( total "{}"    "" )) };
323    (  total_group            ) => { $crate::item!(( total "{:#}"  "" )) };
324    (( total $format:literal )) => { $crate::item!(( total $format "" )) };
325
326    (( total $format:literal $none:literal )) => {
327        $crate::internal::Item::Fn(Box::new(|s| {
328            if let Some(total) = s.total() {
329                format!(
330                    $format,
331                    $crate::internal::FormatInteger::new(total, s.thousands_separator())
332                )
333            } else {
334                $none.to_string()
335            }
336        }))
337    };
338
339    // ============================================================
340    // TOTAL_BIN
341
342    (  total_bin                  ) => { $crate::item!(( total_bin "{:#} {}" "" )) };
343    (( total_bin $format:literal )) => { $crate::item!(( total_bin $format   "" )) };
344
345    (( total_bin $format:literal $none:literal )) => {
346        $crate::internal::Item::Fn(Box::new(|s| {
347            if let Some(total) = s.total() {
348                let (amount, prefix) = $crate::binary_prefix(total as f64);
349                format!(
350                    $format,
351                    $crate::internal::FormatFloat::new(amount, prefix == ""),
352                    $crate::internal::FormatPrefix::new(prefix),
353                )
354            } else {
355                $none.to_string()
356            }
357        }))
358    };
359
360    // ============================================================
361    // TOTAL_DEC
362
363    (  total_dec                  ) => { $crate::item!(( total_dec "{:#} {}" "" )) };
364    (( total_dec $format:literal )) => { $crate::item!(( total_dec $format   "" )) };
365
366    (( total_dec $format:literal $none:literal )) => {
367        $crate::internal::Item::Fn(Box::new(|s| {
368            if let Some(total) = s.total() {
369                let (amount, prefix) = $crate::decimal_prefix(total as f64);
370                format!(
371                    $format,
372                    $crate::internal::FormatFloat::new(amount, prefix == ""),
373                    $crate::internal::FormatPrefix::new(prefix),
374                )
375            } else {
376                $none.to_string()
377            }
378        }))
379    };
380
381    // ============================================================
382    // OTHER
383
384    (( $expr:expr )) => {
385        $crate::internal::Item::Fn(Box::new($expr))
386    };
387
388    ( $literal:literal ) => {
389        $crate::internal::Item::Literal(format!("{}", $literal))
390    };
391}