human_size/
multiples.rs

1//! Module containing all multiples.
2//!
3//! All types defined here implement [`Multiple`]. Because all types defined
4//! here, expect for `Any`, don't have any fields they are always zero sized.
5//! Meaning that for example `SpecificSize<Byte>` has the same size as `f64`
6//! (the type used as underlying value).
7//!
8//! [`Multiple`]: ../trait.Multiple.html
9
10use std::fmt;
11use std::str::FromStr;
12
13use super::{Multiple, ParsingError, SpecificSize};
14
15/// Macro used to allow the `concat` macro to be used inside the doc attribute.
16///
17/// Inspired by the same macro found in the `num` module of Rust's standard
18/// library.
19macro_rules! doc_comment {
20    ($doc:expr, $($tt:tt)*) => {
21        #[doc = $doc]
22        $($tt)*
23    };
24}
25
26/// Macro to create a multiple.
27///
28/// This multiple will be a zero sized struct that implements `Multiple` and
29/// `fmt::Display`.
30macro_rules! multiple {
31    ($name:ident, $size:expr, $str:expr) => {
32        multiple!($name, $size, $str, stringify!($name), stringify!($size));
33    };
34    ($name:ident, $size:expr, $str:expr, $sname:expr, $ssize:expr) => {
35        doc_comment! {
36            concat!("Multiple representing a ", $sname, ".\n\n",
37                    "Represents a size of `value * ", $ssize,
38                    "`. When parsing this multiple from text it expects `",
39                    $str, "`."),
40            #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
41            pub struct $name;
42        }
43
44        impl fmt::Display for $name {
45            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46                f.pad($str)
47            }
48        }
49
50        impl Multiple for $name {
51            fn from_any(value: f64, multiple: Any) -> SpecificSize<Self> {
52                let multiply = multiple.multiple_of_bytes() / $size;
53                let value = value * multiply;
54                SpecificSize {
55                    value,
56                    multiple: $name,
57                }
58            }
59
60            fn into_any(size: SpecificSize<Self>) -> (f64, Any) {
61                (size.value, Any::$name)
62            }
63        }
64
65        impl From<$name> for Any {
66            fn from(_multiple: $name) -> Any {
67                Any::$name
68            }
69        }
70    };
71}
72
73multiple!(Byte, 1_f64, "B");
74
75// Multiples of 1000.
76multiple!(Kilobyte, 1000_f64.powi(1), "kB");
77multiple!(Megabyte, 1000_f64.powi(2), "MB");
78multiple!(Gigabyte, 1000_f64.powi(3), "GB");
79multiple!(Terabyte, 1000_f64.powi(4), "TB");
80multiple!(Petabyte, 1000_f64.powi(5), "PB");
81multiple!(Exabyte, 1000_f64.powi(6), "EB");
82multiple!(Zettabyte, 1000_f64.powi(7), "ZB");
83multiple!(Yottabyte, 1000_f64.powi(8), "YB");
84
85// Multiples of 1024.
86multiple!(Kibibyte, 1024_f64.powi(1), "KiB");
87multiple!(Mebibyte, 1024_f64.powi(2), "MiB");
88multiple!(Gigibyte, 1024_f64.powi(3), "GiB");
89multiple!(Tebibyte, 1024_f64.powi(4), "TiB");
90multiple!(Pebibyte, 1024_f64.powi(5), "PiB");
91multiple!(Exbibyte, 1024_f64.powi(6), "EiB");
92multiple!(Zebibyte, 1024_f64.powi(7), "ZiB");
93multiple!(Yobibyte, 1024_f64.powi(8), "YiB");
94
95/// A multiple which can represent all multiples.
96///
97/// This is mainly used to parse a size from a string, but can also be used when
98/// you don't really care about the multiple or want to maintain the multiple
99/// from the parsed string.
100///
101/// For documentation of each variant see the equivalent struct in this module.
102#[derive(Copy, Clone, Debug, Eq, PartialEq)]
103#[allow(missing_docs)]
104#[non_exhaustive]
105pub enum Any {
106    Byte,
107    Kilobyte,
108    Megabyte,
109    Gigabyte,
110    Terabyte,
111    Petabyte,
112    Exabyte,
113    Zettabyte,
114    Yottabyte,
115    Kibibyte,
116    Mebibyte,
117    Gigibyte,
118    Tebibyte,
119    Pebibyte,
120    Exbibyte,
121    Zebibyte,
122    Yobibyte,
123}
124
125impl Multiple for Any {
126    fn from_any(value: f64, multiple: Any) -> SpecificSize<Self> {
127        SpecificSize { value, multiple }
128    }
129
130    fn into_any(size: SpecificSize<Self>) -> (f64, Any) {
131        (size.value, size.multiple)
132    }
133}
134
135impl Any {
136    pub(crate) fn multiple_of_bytes(self) -> f64 {
137        match self {
138            Any::Byte => 1_f64,
139
140            Any::Kilobyte => 1000_f64,
141            Any::Megabyte => 1000_f64.powi(2),
142            Any::Gigabyte => 1000_f64.powi(3),
143            Any::Terabyte => 1000_f64.powi(4),
144            Any::Petabyte => 1000_f64.powi(5),
145            Any::Exabyte => 1000_f64.powi(6),
146            Any::Zettabyte => 1000_f64.powi(7),
147            Any::Yottabyte => 1000_f64.powi(8),
148
149            Any::Kibibyte => 1024_f64,
150            Any::Mebibyte => 1024_f64.powi(2),
151            Any::Gigibyte => 1024_f64.powi(3),
152            Any::Tebibyte => 1024_f64.powi(4),
153            Any::Pebibyte => 1024_f64.powi(5),
154            Any::Exbibyte => 1024_f64.powi(6),
155            Any::Zebibyte => 1024_f64.powi(7),
156            Any::Yobibyte => 1024_f64.powi(8),
157        }
158    }
159}
160
161impl FromStr for Any {
162    type Err = ParsingError;
163
164    fn from_str(input: &str) -> Result<Any, Self::Err> {
165        // Special case this to mean kibibytes.
166        // FIXME(#10): this is not ideal.
167        if input == "KB" {
168            return Ok(Any::Kibibyte);
169        }
170
171        let units = [
172            ("B", Any::Byte),
173            ("Kb", Any::Kilobyte),
174            ("MB", Any::Megabyte),
175            ("GB", Any::Gigabyte),
176            ("TB", Any::Terabyte),
177            ("PB", Any::Petabyte),
178            ("EB", Any::Exabyte),
179            ("ZB", Any::Zettabyte),
180            ("YB", Any::Yottabyte),
181            ("KIB", Any::Kibibyte),
182            ("MIB", Any::Mebibyte),
183            ("GIB", Any::Gigibyte),
184            ("TIB", Any::Tebibyte),
185            ("PIB", Any::Pebibyte),
186            ("EIB", Any::Exbibyte),
187            ("ZIB", Any::Zebibyte),
188            ("YIB", Any::Yobibyte),
189        ];
190
191        for (name, variant) in units {
192            if input.eq_ignore_ascii_case(name) {
193                return Ok(variant);
194            }
195        }
196
197        Err(ParsingError::InvalidMultiple)
198    }
199}
200
201impl fmt::Display for Any {
202    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
203        f.pad(match *self {
204            Any::Byte => "B",
205
206            Any::Kilobyte => "kB",
207            Any::Megabyte => "MB",
208            Any::Gigabyte => "GB",
209            Any::Terabyte => "TB",
210            Any::Petabyte => "PB",
211            Any::Exabyte => "EB",
212            Any::Zettabyte => "ZB",
213            Any::Yottabyte => "YB",
214
215            Any::Kibibyte => "KiB",
216            Any::Mebibyte => "MiB",
217            Any::Gigibyte => "GiB",
218            Any::Tebibyte => "TiB",
219            Any::Pebibyte => "PiB",
220            Any::Exbibyte => "EiB",
221            Any::Zebibyte => "ZiB",
222            Any::Yobibyte => "YiB",
223        })
224    }
225}