oxide_update_engine_display/utils.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Utility displayers.
6
7use std::fmt;
8
9/// Given current and total, displays `{current}/{total}`.
10///
11/// * If the `index_and_total` constructor is called, then `current` is `index
12/// + 1`.
13/// * If `padded` is `true`, `current` is right-aligned and padded with spaces
14/// to the width of `total`.
15///
16/// # Examples
17///
18/// ```
19/// use oxide_update_engine_display::ProgressRatioDisplay;
20///
21/// // 0-based index and total.
22/// let display = ProgressRatioDisplay::index_and_total(0 as u64, 8 as u64);
23/// assert_eq!(display.to_string(), "1/8");
24///
25/// // 1-based current and total.
26/// let display = ProgressRatioDisplay::current_and_total(82 as u64, 230 as u64);
27/// assert_eq!(display.to_string(), "82/230");
28///
29/// // With padding.
30/// let display = display.padded(true);
31/// assert_eq!(display.to_string(), " 82/230");
32/// ```
33#[derive(Debug)]
34pub struct ProgressRatioDisplay {
35 current: u64,
36 total: u64,
37 padded: bool,
38}
39
40impl ProgressRatioDisplay {
41 /// Create a new `ProgressRatioDisplay` with current and total values.
42 ///
43 /// `current` is considered to be 1-based. For example, "20/80 jobs done".
44 pub fn current_and_total<T: ToU64>(current: T, total: T) -> Self {
45 Self { current: current.to_u64(), total: total.to_u64(), padded: false }
46 }
47
48 /// Create a new `ProgressRatioDisplay` with index and total values.
49 ///
50 /// The index is 0-based (i.e. 1 is added to it). For example, step index 0
51 /// out of 8 total steps is shown as "1/8".
52 pub fn index_and_total<T: ToU64>(index: T, total: T) -> Self {
53 Self {
54 current: index
55 .to_u64()
56 .checked_add(1)
57 .expect("index can't be u64::MAX"),
58 total: total.to_u64(),
59 padded: false,
60 }
61 }
62
63 /// If set to true, the current value is padded to the same width as the
64 /// total.
65 pub fn padded(self, padded: bool) -> Self {
66 Self { padded, ..self }
67 }
68}
69
70impl fmt::Display for ProgressRatioDisplay {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 if self.padded {
73 let width = self.total.to_string().len();
74 write!(f, "{:>width$}/{}", self.current, self.total)
75 } else {
76 write!(f, "{}/{}", self.current, self.total)
77 }
78 }
79}
80
81/// Trait that abstracts over `usize` and `u64`.
82///
83/// There are no `From` implementations between `usize` and `u64`, but we
84/// assert below that all the architectures we support are 64-bit.
85pub trait ToU64 {
86 fn to_u64(self) -> u64;
87}
88
89const _: () = {
90 assert!(
91 std::mem::size_of::<usize>() <= std::mem::size_of::<u64>(),
92 "usize and u64 are the same size"
93 );
94};
95
96impl ToU64 for usize {
97 #[inline]
98 fn to_u64(self) -> u64 {
99 self as u64
100 }
101}
102
103impl ToU64 for u64 {
104 #[inline]
105 fn to_u64(self) -> u64 {
106 self
107 }
108}