1#![doc(html_root_url = "https://docs.rs/human_format")]
2
3#[derive(Debug)]
46struct ScaledValue {
47 value: f64,
48 suffix: String,
49}
50
51#[derive(Debug)]
53pub struct Formatter {
54 decimals: usize,
55 separator: String,
56 scales: Scales,
57 forced_units: String,
58 forced_suffix: String,
59}
60
61impl Default for Formatter {
62 fn default() -> Self {
63 Formatter {
64 decimals: 2,
65 separator: " ".to_owned(),
66 scales: Scales::SI(),
67 forced_units: "".to_owned(),
68 forced_suffix: "".to_owned(),
69 }
70 }
71}
72
73#[derive(Debug)]
75pub struct Scales {
76 base: u32,
77 suffixes: Vec<String>,
78}
79
80impl Formatter {
81 pub fn new() -> Self {
83 Default::default()
84 }
85
86 pub fn with_decimals(&mut self, decimals: usize) -> &mut Self {
88 self.decimals = decimals;
89
90 self
91 }
92
93 pub fn with_separator(&mut self, separator: &str) -> &mut Self {
95 self.separator = separator.to_owned();
96
97 self
98 }
99
100 pub fn with_scales(&mut self, scales: Scales) -> &mut Self {
102 self.scales = scales;
103
104 self
105 }
106
107 pub fn with_units(&mut self, units: &str) -> &mut Self {
109 self.forced_units = units.to_owned();
110
111 self
112 }
113
114 pub fn with_suffix(&mut self, suffix: &str) -> &mut Self {
116 self.forced_suffix = suffix.to_owned();
117
118 self
119 }
120
121 pub fn format(&self, value: f64) -> String {
123 if value < 0.0 {
124 return format!("-{}", self.format(value * -1.0));
125 }
126
127 let scaled_value = self.scales.to_scaled_value(value);
128
129 format!(
130 "{:.width$}{}{}{}",
131 scaled_value.value,
132 self.separator,
133 scaled_value.suffix,
134 self.forced_units,
135 width = self.decimals
136 )
137 }
138
139 pub fn parse(&self, value: &str) -> f64 {
141 let v: Vec<&str> = value.split(&self.separator).collect();
142
143 let result = v.first().unwrap().parse::<f64>().unwrap();
144
145 let mut suffix = v.get(1).unwrap().to_string();
146 let new_len = suffix.len() - self.forced_units.len();
147
148 suffix.truncate(new_len);
149
150 let magnitude_multiplier = self.scales.get_magnitude_multiplier(&suffix);
151
152 result * magnitude_multiplier
153 }
154
155 pub fn try_parse(&self, value: &str) -> Result<f64, String> {
157 let value = value.trim_end_matches(&self.forced_units).to_string();
159
160 let mut number = String::new();
162 for c in value.chars() {
163 if c.is_digit(10) || c == '.' {
164 number.push(c);
165 } else {
166 break;
167 }
168 }
169
170 let suffix = value
171 .trim_start_matches(&number)
172 .trim_start_matches(&self.separator)
173 .to_string();
174
175 let number = number.parse::<f64>().map_err(|e| e.to_string())?;
176 let magnitude_multiplier = self.scales.try_get_magnitude_multiplier(&suffix)?;
177
178 Ok(number * magnitude_multiplier)
179 }
180}
181
182impl Default for Scales {
183 fn default() -> Self {
184 Scales::SI()
185 }
186}
187
188impl Scales {
189 pub fn new() -> Self {
191 Scales::SI()
192 }
193
194 #[allow(non_snake_case)]
196 pub fn SI() -> Self {
197 Scales {
198 base: 1000,
199 suffixes: vec![
200 "".to_owned(),
201 "K".to_owned(),
202 "M".to_owned(),
203 "G".to_owned(),
204 "T".to_owned(),
205 "P".to_owned(),
206 "E".to_owned(),
207 "Z".to_owned(),
208 "Y".to_owned(),
209 ],
210 }
211 }
212
213 #[allow(non_snake_case)]
215 pub fn Binary() -> Self {
216 Scales {
217 base: 1024,
218 suffixes: vec![
219 "".to_owned(),
220 "Ki".to_owned(),
221 "Mi".to_owned(),
222 "Gi".to_owned(),
223 "Ti".to_owned(),
224 "Pi".to_owned(),
225 "Ei".to_owned(),
226 "Zi".to_owned(),
227 "Yi".to_owned(),
228 ],
229 }
230 }
231
232 pub fn with_base(&mut self, base: u32) -> &mut Self {
234 self.base = base;
235
236 self
237 }
238
239 pub fn with_suffixes(&mut self, suffixes: Vec<&str>) -> &mut Self {
241 self.suffixes = Vec::new();
242
243 for suffix in suffixes {
244 self.suffixes.push(suffix.to_owned());
247 }
248
249 self
250 }
251
252 fn try_get_magnitude_multiplier(&self, value: &str) -> Result<f64, String> {
253 self.suffixes
254 .iter()
255 .enumerate()
256 .find_map(|(idx, x)| {
257 if value == x {
258 Some((self.base as f64).powi(idx as i32))
259 } else {
260 None
261 }
262 })
263 .ok_or_else(|| {
264 format!(
265 "Unknown suffix: {value}, valid suffixes are: {}",
266 self.suffixes
267 .iter()
268 .filter(|x| !x.trim().is_empty())
269 .map(String::to_string)
270 .collect::<Vec<_>>()
271 .join(", ")
272 )
273 })
274 }
275
276 fn get_magnitude_multiplier(&self, value: &str) -> f64 {
277 for ndx in 0..self.suffixes.len() {
278 if value == self.suffixes[ndx] {
279 return (self.base as f64).powi(ndx as i32);
280 }
281 }
282
283 0.0
284 }
285
286 fn to_scaled_value(&self, value: f64) -> ScaledValue {
287 let mut index: usize = 0;
288 let base: f64 = self.base as f64;
289 let mut value = value;
290
291 loop {
292 if value < base {
293 break;
294 }
295
296 value /= base;
297 index += 1;
298 }
299
300 ScaledValue {
301 value,
302 suffix: self.suffixes[index].to_owned(),
303 }
304 }
305}