si_scale/
helpers.rs

1//! The helpers functions provide number parsing and correct SI formatting for
2//! various units. They are probably the most used functions in this crate.
3//!
4//! You can extend with your own units and formatting using the
5//! `scale_fn!()` macro.
6//!
7//! The `seconds()` function parses a number into a `Value` and displays it
8//! using 3 decimals and the appropriate scale for seconds (`UnitAndBelow`),
9//! so that non-sensical scales such as kilo-seconds may not appear.
10//!
11//! ```
12//! use si_scale::helpers::{seconds, seconds3};
13//!
14//! let actual = format!("result is {}", seconds(1234.5678));
15//! let expected = "result is 1234.5678 s";
16//! assert_eq!(actual, expected);
17//!
18//! let actual = format!("result is {:>10}", seconds3(12.3e-7));
19//! let expected = "result is   1.230 µs";
20//! assert_eq!(actual, expected);
21//! ```
22//!
23//! The `bytes1()` function parses a number into a `Value` *using base 1000*
24//! and displays it using 1 decimal and the appropriate scale for bytes
25//! (`UnitAndAbove`), so that non-sensical scales such as milli-bytes may not
26//! appear.
27//!
28//! ```
29//! use si_scale::helpers::bytes1;
30//!
31//! let actual = format!("result is {}", bytes1(12_345_678));
32//! let expected = "result is 12.3 MB";
33//! assert_eq!(actual, expected);
34//!
35//! let actual = format!("result is {:>10}", bytes1(16));
36//! let expected = "result is     16.0 B";
37//! assert_eq!(actual, expected);
38//!
39//! let actual = format!("result is {}", bytes1(0.12));
40//! let expected = "result is 0.1 B";
41//! assert_eq!(actual, expected);
42//! ```
43//!
44//! The `bibytes1()` function parses a number into a `Value` *using base 1024*
45//! and displays it using 1 decimal and the appropriate scale for bytes
46//! (`UnitAndAbove`), so that non-sensical scales such as milli-bytes may not
47//! appear.
48//!
49//! ```
50//! use si_scale::helpers::bibytes1;
51//!
52//! let actual = format!("result is {}", bibytes1(12_345_678));
53//! let expected = "result is 11.8 MiB";
54//! assert_eq!(actual, expected);
55
56//! let actual = format!("result is {}", bibytes1(16 * 1024));
57//! let expected = "result is 16.0 kiB";
58//! assert_eq!(actual, expected);
59
60//! let actual = format!("result is {:>10}", bibytes1(16));
61//! let expected = "result is     16.0 B";
62//! assert_eq!(actual, expected);
63
64//! let actual = format!("result is {}", bibytes1(0.12));
65//! let expected = "result is 0.1 B";
66//! assert_eq!(actual, expected);
67//! ```
68
69/// Three nearly identical variants: with the unit argument only, with unit and
70/// groupings arguments, with groupings argument only. If you happen to know how
71/// to factor this, please make a suggestion!
72#[macro_export]
73macro_rules! scale_fn {
74    (
75        $name:ident,
76        base: $base_arg:ident,
77        constraint: $constraint_arg:ident,
78        mantissa_fmt: $mantissa_fmt:expr,
79        unit: $unit_arg:literal,
80        doc: $doc_arg:literal
81    ) => {
82        #[doc=$doc_arg]
83        pub fn $name<F>(x: F) -> String
84        where
85            F: Into<f64>,
86        {
87            let value = $crate::value::Value::new_with(
88                x,
89                $crate::base::Base::$base_arg,
90                $crate::prefix::Constraint::$constraint_arg,
91            );
92            format!(
93                "{}{}",
94                $crate::format_value!(value, $mantissa_fmt),
95                $unit_arg
96            )
97        }
98    };
99
100    (
101        $name:ident,
102        base: $base_arg:ident,
103        constraint: $constraint_arg:ident,
104        mantissa_fmt: $mantissa_fmt:expr,
105        groupings: $sep_arg:literal,
106        unit: $unit_arg:literal,
107        doc: $doc_arg:literal
108    ) => {
109        #[doc=$doc_arg]
110        pub fn $name<F>(x: F) -> String
111        where
112            F: Into<f64>,
113        {
114            let value = $crate::value::Value::new_with(
115                x,
116                $crate::base::Base::$base_arg,
117                $crate::prefix::Constraint::$constraint_arg,
118            );
119            format!(
120                "{}{}",
121                $crate::format_value!(value, $mantissa_fmt, groupings: $sep_arg),
122                $unit_arg
123            )
124        }
125    };
126
127    (
128        $name:ident,
129        base: $base_arg:ident,
130        constraint: $constraint_arg:ident,
131        mantissa_fmt: $mantissa_fmt:expr,
132        groupings: $sep_arg:literal,
133        doc: $doc_arg:literal
134    ) => {
135        #[doc=$doc_arg]
136        pub fn $name<F>(x: F) -> String
137        where
138            F: Into<f64>,
139        {
140            let value = $crate::value::Value::new_with(
141                x,
142                $crate::base::Base::$base_arg,
143                $crate::prefix::Constraint::$constraint_arg,
144            );
145            format!(
146                "{}",
147                $crate::format_value!(value, $mantissa_fmt, groupings: $sep_arg, no_unit)
148            )
149        }
150    };
151}
152
153scale_fn!(number_,
154          base: B1000,
155          constraint: UnitOnly,
156          mantissa_fmt: "{}",
157          groupings: '_',
158          doc: "Print a number without units.");
159
160scale_fn!(seconds,
161          base: B1000,
162          constraint: UnitAndBelow,
163          mantissa_fmt: "{}",
164          unit: "s",
165          doc: "Print a value in seconds.");
166
167scale_fn!(seconds3,
168          base: B1000,
169          constraint: UnitAndBelow,
170          mantissa_fmt: "{:.3}",
171          unit: "s",
172          doc: "Print a value in seconds with 3 decimals.");
173
174scale_fn!(bytes,
175          base: B1000,
176          constraint: UnitAndAbove,
177          mantissa_fmt: "{}",
178          unit: "B",
179          doc: "Print a value in bytes.");
180
181scale_fn!(bytes_,
182          base: B1000,
183          constraint: UnitOnly,
184          mantissa_fmt: "{}",
185          groupings: '_',
186          unit: "B",
187          doc: "Print a value in bytes with thousands separator.");
188
189scale_fn!(bytes1,
190          base: B1000,
191          constraint: UnitAndAbove,
192          mantissa_fmt: "{:.1}",
193          unit: "B",
194          doc: "Print a value in bytes with 1 decimal.");
195
196scale_fn!(bytes2,
197          base: B1000,
198          constraint: UnitAndAbove,
199          mantissa_fmt: "{:.2}",
200          unit: "B",
201          doc: "Print a value in bytes with 2 decimals.");
202
203scale_fn!(bibytes,
204          base: B1024,
205          constraint: UnitAndAbove,
206          mantissa_fmt: "{}",
207          unit: "B",
208          doc: "Print a value in bibytes.");
209
210scale_fn!(bibytes1,
211          base: B1024,
212          constraint: UnitAndAbove,
213          mantissa_fmt: "{:.1}",
214          unit: "B",
215          doc: "Print a value in bibytes with 1 decimal.");
216
217scale_fn!(bibytes2,
218          base: B1024,
219          constraint: UnitAndAbove,
220          mantissa_fmt: "{:.2}",
221          unit: "B",
222          doc: "Print a value in bibytes with 2 decimals.");
223
224#[cfg(test)]
225mod tests {
226    use super::*;
227
228    #[test]
229    fn test_number_() {
230        let actual = format!("result is {}", number_(1234.5678));
231        let expected = "result is 1_234.567_8";
232        assert_eq!(actual, expected);
233
234        let actual = format!("result is {:>10}", number_(12.4e-8));
235        let expected = "result is 0.000_000_124";
236        assert_eq!(actual, expected);
237
238        let actual = format!("result is {}", number_(1.1));
239        let expected = "result is 1.1";
240        assert_eq!(actual, expected);
241
242        let actual = format!("result is {}", number_(1.0));
243        let expected = "result is 1";
244        assert_eq!(actual, expected);
245    }
246
247    #[test]
248    fn test_seconds() {
249        let actual = format!("result is {}", seconds(1234.5678));
250        let expected = "result is 1234.5678 s";
251        assert_eq!(actual, expected);
252
253        let actual = format!("result is {:>10}", seconds(12.4e-7));
254        let expected = "result is    1.24 µs";
255        assert_eq!(actual, expected);
256
257        let actual = format!("result is {}", seconds(12e-7));
258        let expected = "result is 1.2 µs";
259        assert_eq!(actual, expected);
260
261        let actual = format!("result is {}", seconds(1.0));
262        let expected = "result is 1 s";
263        assert_eq!(actual, expected);
264    }
265
266    #[test]
267    fn test_seconds3() {
268        let actual = format!("result is {}", seconds3(1234.5678));
269        let expected = "result is 1234.568 s";
270        assert_eq!(actual, expected);
271
272        let actual = format!("result is {:>10}", seconds3(12.4e-7));
273        let expected = "result is   1.240 µs";
274        assert_eq!(actual, expected);
275
276        let actual = format!("result is {}", seconds3(12e-7));
277        let expected = "result is 1.200 µs";
278        assert_eq!(actual, expected);
279
280        let actual = format!("result is {}", seconds3(1.0));
281        let expected = "result is 1.000 s";
282        assert_eq!(actual, expected);
283    }
284
285    #[test]
286    fn test_bytes() {
287        let actual = format!("result is {}", bytes(12_345_678));
288        let expected = "result is 12.345678 MB";
289        assert_eq!(actual, expected);
290
291        let actual = format!("result is {:>10}", bytes(16));
292        let expected = "result is       16 B";
293        assert_eq!(actual, expected);
294
295        let actual = format!("result is {}", bytes(0.123456));
296        let expected = "result is 0.123456 B";
297        assert_eq!(actual, expected);
298    }
299
300    #[test]
301    fn test_bytes_() {
302        let actual = format!("result is {}", bytes_(12_345_678));
303        let expected = "result is 12_345_678 B";
304        assert_eq!(actual, expected);
305
306        let actual = format!("result is {:>10}", bytes_(16));
307        let expected = "result is       16 B";
308        assert_eq!(actual, expected);
309
310        let actual = format!("result is {}", bytes_(0.123456));
311        let expected = "result is 0.123_456 B";
312        assert_eq!(actual, expected);
313    }
314
315    #[test]
316    fn test_bytes1() {
317        let actual = format!("result is {}", bytes1(12_345_678));
318        let expected = "result is 12.3 MB";
319        assert_eq!(actual, expected);
320
321        let actual = format!("result is {:>10}", bytes1(16));
322        let expected = "result is     16.0 B";
323        assert_eq!(actual, expected);
324
325        let actual = format!("result is {}", bytes1(0.12));
326        let expected = "result is 0.1 B";
327        assert_eq!(actual, expected);
328    }
329
330    #[test]
331    fn test_bibytes() {
332        let actual = format!("result is {}", bibytes(11.8 * (1024 * 1024) as f64));
333        let expected = "result is 11.8 MiB";
334        assert_eq!(actual, expected);
335
336        let actual = format!("result is {}", bibytes(16 * 1024));
337        let expected = "result is 16 kiB";
338        assert_eq!(actual, expected);
339
340        let actual = format!("result is {:>10}", bibytes(16));
341        let expected = "result is       16 B";
342        assert_eq!(actual, expected);
343
344        let actual = format!("result is {}", bibytes(0.123456));
345        let expected = "result is 0.123456 B";
346        assert_eq!(actual, expected);
347    }
348
349    #[test]
350    fn test_bibytes1() {
351        let actual = format!("result is {}", bibytes1(12_345_678));
352        let expected = "result is 11.8 MiB";
353        assert_eq!(actual, expected);
354
355        let actual = format!("result is {}", bibytes1(16 * 1024));
356        let expected = "result is 16.0 kiB";
357        assert_eq!(actual, expected);
358
359        let actual = format!("result is {:>10}", bibytes1(16));
360        let expected = "result is     16.0 B";
361        assert_eq!(actual, expected);
362
363        let actual = format!("result is {}", bibytes1(0.12));
364        let expected = "result is 0.1 B";
365        assert_eq!(actual, expected);
366    }
367
368    #[test]
369    fn test_issue_8() {
370        let actual = format!("result is {}", seconds3(178.844052305));
371        let expected = "result is 178.844 s";
372        assert_eq!(actual, expected);
373
374        let actual = format!("result is {}", seconds3(83.99999999999999e-9));
375        let expected = "result is 84.000 ns";
376        assert_eq!(actual, expected);
377    }
378}