ml_progress/
internal.rs

1//! Internal types.
2//!
3//! These are not meant to be used directly
4//! but need to be public for macros to work.
5
6use std::fmt;
7
8use crate::State;
9
10// ======================================================================
11// Item - PUBLIC
12
13/// _Internal_ An item shown on progress indicator line.
14pub enum Item {
15    Fill(FillItem),
16    Fn(Box<dyn Fn(&State) -> String + Send + Sync>),
17    Literal(String),
18}
19
20// ======================================================================
21// FillItem - PUBLIC
22
23/// _Internal_ An item which fills remaining space on the line.
24pub enum FillItem {
25    Bar,
26    Message,
27}
28
29// ======================================================================
30// FormatFloat - PUBLIC
31
32/// _Internal_ Wrapper for custom formatting of `f64`.
33pub struct FormatFloat {
34    value: f64,
35    // This applies only if `#` flag is used.
36    ignore_precision: bool,
37}
38
39impl FormatFloat {
40    pub fn new(value: f64, ignore_precision: bool) -> Self {
41        // I don't need negative values for now.
42        assert!(value >= 0.0);
43
44        Self {
45            value,
46            ignore_precision,
47        }
48    }
49}
50
51// ======================================================================
52// FormatFloat - IMPL DISPLAY
53
54impl fmt::Display for FormatFloat {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        if f.alternate() {
57            if self.ignore_precision {
58                f.pad_integral(true, "", &format!("{:.0}", self.value))
59            } else {
60                let scale = if self.value < 10.0 {
61                    0
62                } else {
63                    self.value.log10().floor() as usize
64                };
65
66                let fit_width = f.precision().unwrap_or(4);
67                let precision = fit_width.saturating_sub(scale + 2);
68                f.pad_integral(true, "", &format!("{:.*}", precision, self.value))
69            }
70        } else {
71            self.value.fmt(f)
72        }
73    }
74}
75
76// ======================================================================
77// FormatInteger - PUBLIC
78
79/// _Internal_ Wrapper for custom formatting of `u64`.
80pub struct FormatInteger<'a> {
81    value: u64,
82    thousands_separator: &'a str,
83}
84
85impl<'a> FormatInteger<'a> {
86    pub fn new(value: u64, thousands_separator: &'a str) -> Self {
87        Self {
88            value,
89            thousands_separator,
90        }
91    }
92}
93
94// ======================================================================
95// FormatInteger - IMPL DISPLAY
96
97impl<'a> fmt::Display for FormatInteger<'a> {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        if f.alternate() {
100            f.pad_integral(
101                true,
102                "",
103                &crate::group_digits(self.value, self.thousands_separator),
104            )
105        } else {
106            self.value.fmt(f)
107        }
108    }
109}
110
111// ======================================================================
112// FormatPrefix - PUBLIC
113
114/// _Internal_ Wrapper for custom formatting of prefix.
115pub struct FormatPrefix(&'static str);
116
117impl FormatPrefix {
118    pub fn new(value: &'static str) -> Self {
119        Self(value)
120    }
121}
122
123// ======================================================================
124// FormatPrefix - IMPL DISPLAY
125
126impl fmt::Display for FormatPrefix {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        if f.alternate() {
129            if self.0.is_empty() {
130                Ok(())
131            } else {
132                write!(f, " {}", self.0)
133            }
134        } else {
135            self.0.fmt(f)
136        }
137    }
138}
139
140// ======================================================================
141// TESTS
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    // ============================================================
148    // FormatFloat
149
150    #[test]
151    fn format_float() {
152        assert_eq!(format!("{:#.4}", FormatFloat::new(1.23456, false)), "1.23");
153        assert_eq!(format!("{:#.4}", FormatFloat::new(12.3456, false)), "12.3");
154        assert_eq!(format!("{:#.4}", FormatFloat::new(123.456, false)), "123");
155        assert_eq!(format!("{:#.4}", FormatFloat::new(1234.56, false)), "1235");
156        assert_eq!(format!("{:#.4}", FormatFloat::new(12345.6, false)), "12346");
157    }
158
159    #[test]
160    fn format_float_0() {
161        assert_eq!(format!("{:#.4}", FormatFloat::new(0.0, false)), "0.00");
162    }
163
164    #[test]
165    fn format_float_ignore_precision() {
166        assert_eq!(format!("{:#.4}", FormatFloat::new(12.34, true)), "12");
167    }
168}