1use std::fmt;
11use std::str::FromStr;
12
13use super::{Multiple, ParsingError, SpecificSize};
14
15macro_rules! doc_comment {
20 ($doc:expr, $($tt:tt)*) => {
21 #[doc = $doc]
22 $($tt)*
23 };
24}
25
26macro_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
75multiple!(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
85multiple!(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#[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 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}