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}