num_runtime_fmt/
builder.rs

1use super::{Align, Base, NumFmt, Sign};
2
3/// Builder for a numeric formatter.
4#[derive(Clone, PartialEq, Eq, Debug, Default)]
5pub struct Builder {
6    fill: Option<char>,
7    align: Align,
8    sign: Sign,
9    hash: bool,
10    zero: bool,
11    width: usize,
12    precision: Option<usize>,
13    format: Base,
14    separator: Option<char>,
15    spacing: Option<usize>,
16    decimal_separator: Option<char>,
17}
18
19impl Builder {
20    /// Construct a new `Builder`.
21    pub fn new() -> Builder {
22        Self::default()
23    }
24
25    /// Build a [`NumFmt`] instance, consuming this builder.
26    pub fn build(self) -> NumFmt {
27        let Builder {
28            fill,
29            align,
30            sign,
31            hash,
32            zero,
33            width,
34            precision,
35            format,
36            separator,
37            spacing,
38            decimal_separator,
39        } = self;
40        NumFmt {
41            fill,
42            align,
43            sign,
44            hash,
45            zero,
46            width,
47            precision,
48            base: format,
49            separator,
50            spacing,
51            decimal_separator,
52        }
53    }
54
55    /// When `width` is greater than the actual rendered width of the number, the excess is padded
56    /// with this character.
57    ///
58    /// ## Note
59    /// Wide characters are counted according to their quantity, not their bit width.
60    ///
61    /// ```rust
62    /// # use num_runtime_fmt::NumFmt;
63    /// let heart = '🖤';
64    /// assert_eq!(heart.len_utf8(), 4);
65    /// let fmt = NumFmt::builder().fill(heart).width(3).build();
66    /// let formatted = fmt.fmt(1).unwrap();
67    /// assert_eq!(formatted, "🖤🖤1");
68    /// // Note that even though we requested a width of 3, the binary length is 9.
69    /// assert_eq!(formatted.len(), 9);
70    /// ```
71    #[inline]
72    pub fn fill(mut self, param: char) -> Self {
73        self.fill = Some(param);
74        self
75    }
76
77    /// Set the alignment of rendering within allotted `width`. See [`Align`].
78    #[inline]
79    pub fn align(mut self, param: Align) -> Self {
80        self.align = param;
81        self
82    }
83
84    /// Set the rendering of the sign. See [`Sign`].
85    #[inline]
86    pub fn sign(mut self, param: Sign) -> Self {
87        self.sign = param;
88        self
89    }
90
91    /// If a `set`, print a base specification before the number
92    /// according to its format.
93    ///
94    /// See [`Builder::format`].
95    ///
96    /// - binary: `0b`
97    /// - octal: `0o`
98    /// - decimal: `0d`
99    /// - hex: `0x`
100    ///
101    /// Corresponds to the `#` format specifier.
102    #[inline]
103    pub fn hash(mut self, set: bool) -> Self {
104        self.hash = set;
105        self
106    }
107
108    /// If `set`, engage the zero handler.
109    ///
110    /// The zero handler overrides the padding specification to `0`, and
111    /// treats pad characters as part of the number, in contrast
112    /// to the default behavior which treats them as arbitrary spacing.
113    ///
114    /// Only valid with `Align::Right` and `Align::Decimal`.
115    ///
116    /// ## Examples
117    ///
118    /// ```rust
119    /// # use num_runtime_fmt::NumFmt;
120    /// // sign handling
121    /// assert_eq!(NumFmt::from_str("03").unwrap().fmt(-1).unwrap(),  "-01");
122    /// assert_eq!(NumFmt::from_str("0>3").unwrap().fmt(-1).unwrap(), "0-1");
123    /// ```
124    ///
125    /// ```rust
126    /// # use num_runtime_fmt::NumFmt;
127    /// // separator handling
128    /// assert_eq!(NumFmt::from_str("0>7,").unwrap().fmt(1).unwrap(), "0000001");
129    /// assert_eq!(NumFmt::from_str("07,").unwrap().fmt(1).unwrap(),  "000,001");
130    /// ```
131    #[inline]
132    pub fn zero(mut self, set: bool) -> Self {
133        if set {
134            self.fill = Some('0');
135            self.zero = true;
136        } else {
137            self.fill = None;
138            self.zero = false;
139        }
140        self
141    }
142
143    /// Set the `width` parameter.
144    ///
145    /// This is a parameter for the "minimum width" that the format should take up. If
146    /// the value's string does not fill up this many characters, then the padding
147    /// specified by fill/alignment will be used to take up the required space (see
148    /// [`Builder::fill`]).
149    ///
150    /// The width can be set dynamically:
151    ///
152    /// ```rust
153    /// # use num_runtime_fmt::{NumFmt, Dynamic};
154    /// assert_eq!(NumFmt::from_str("-^").unwrap().fmt_with(1, Dynamic::width(5)).unwrap(), "--1--");
155    /// ```
156    ///
157    /// Note: with `Align::Right`, this is the minimum width of the entire rendered field,
158    /// not just the portion before the decimal. To set the width before the decimal,
159    /// use `Align::Decimal`.
160    ///
161    /// ```rust
162    /// # use num_runtime_fmt::{NumFmt, Dynamic};
163    /// assert_eq!(NumFmt::from_str( "05").unwrap().fmt(1.1).unwrap(),   "001.1");
164    /// assert_eq!(NumFmt::from_str(">05").unwrap().fmt(1.1).unwrap(),   "001.1");
165    /// assert_eq!(NumFmt::from_str("v05").unwrap().fmt(1.1).unwrap(), "00001.1");
166    /// ```
167    #[inline]
168    pub fn width(mut self, param: usize) -> Self {
169        self.width = param;
170        self
171    }
172
173    /// Set the `precision` parameter.
174    ///
175    /// How many digits after the decimal point are printed. Note that integers can be forced
176    /// to emit decimal places with this modifier.
177    ///
178    /// Precision will pad or truncate as required if set. If unset, passes through as many
179    /// digits past the decimal as the underlying type naturally returns.
180    ///
181    /// ```rust
182    /// # use num_runtime_fmt::{NumFmt, Dynamic};
183    /// assert_eq!(NumFmt::from_str(".2").unwrap().fmt(3.14159).unwrap(), "3.14");
184    /// assert_eq!(NumFmt::from_str(".7").unwrap().fmt(3.14159).unwrap(), "3.1415900");
185    /// ```
186    ///
187    /// If the requested precision exceeds the native precision available to this number,
188    /// the remainder is always filled with `'0'`, even if `fill` is specified:
189    ///
190    /// ```rust
191    /// # use num_runtime_fmt::NumFmt;
192    /// assert_eq!(NumFmt::from_str("-<6.2").unwrap().fmt(1.0_f32).unwrap(), "1.00--");
193    /// ```
194    #[inline]
195    pub fn precision(mut self, param: Option<usize>) -> Self {
196        self.precision = param;
197        self
198    }
199
200    /// Set the output format.
201    ///
202    /// See [`Base`].
203    #[inline]
204    pub fn base(mut self, param: Base) -> Self {
205        self.format = param;
206        self
207    }
208
209    /// Set the separator.
210    ///
211    /// A separator is a (typically non-numeric) character inserted between groups of digits to make
212    /// it easier for humans to parse the number when reading. Different separators may
213    /// be desirable in different contexts.
214    #[inline]
215    pub fn separator(mut self, param: Option<char>) -> Self {
216        self.separator = param;
217        self
218    }
219
220    /// Set the spacing.
221    ///
222    /// Spacing determines the number of characters in each character group. It is only
223    /// of interest when the separator is set. The default spacing is 3.
224    #[inline]
225    pub fn spacing(mut self, param: usize) -> Self {
226        self.spacing = Some(param);
227        self
228    }
229
230    /// Set the decimal separator.
231    ///
232    /// This can be desirable to i.e. support German number formats, which use a `.` to separate
233    /// numeric groups and a `,` as a decimal separator.
234    #[inline]
235    pub fn decimal_separator(mut self, param: char) -> Self {
236        self.decimal_separator = Some(param);
237        self
238    }
239}
240
241impl From<NumFmt> for Builder {
242    fn from(
243        NumFmt {
244            fill,
245            align,
246            sign,
247            hash,
248            zero,
249            width,
250            precision,
251            base: format,
252            separator,
253            spacing,
254            decimal_separator,
255        }: NumFmt,
256    ) -> Self {
257        Builder {
258            fill,
259            align,
260            sign,
261            hash,
262            zero,
263            width,
264            precision,
265            format,
266            separator,
267            spacing,
268            decimal_separator,
269        }
270    }
271}