Skip to main content

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}