1use crate::prefixes::{from_magnitude, MAGNITUDES, PATTERNS};
2use num_format::{Locale, ToFormattedString};
3use regex::{Regex, RegexSet};
4
5pub fn get_match(s: &str) -> Option<usize> {
6 let set = RegexSet::new(&PATTERNS).unwrap();
7 let matches: Vec<usize> = set.matches(s.trim_end()).into_iter().collect();
8 if matches.len() != 1 {
9 return None;
10 }
11 matches.get(0).copied()
12}
13
14pub fn get_magnitude(s: &str) -> i8 {
15 get_match(s).map_or(0, |m| MAGNITUDES[m])
16}
17
18pub fn clean(x: &str) -> String {
19 Regex::new(r"(?:[,_])|^(0|\s)+\b|(?i:\s|[μa-z])+$")
20 .unwrap()
21 .replace_all(x, "")
22 .to_string()
23}
24
25pub fn parse(with_units: &str, magnitude: i8) -> Option<String> {
26 let maginitude = magnitude + get_magnitude(with_units);
27 let num_str = clean(with_units);
28 let mut parts = num_str.split('.');
29 let whole_part = parts.next().unwrap();
30 let fraction_part = parts.next().unwrap_or("");
31
32 if parts.next().is_some() {
35 return None;
36 }
37 if maginitude == 0 && !fraction_part.is_empty() {
38 return None;
39 }
40 if fraction_part.len() as i8 > maginitude {
41 return None;
42 }
43
44 return Some(format!(
45 "{}{}{}",
46 whole_part,
47 fraction_part,
48 "0".repeat(maginitude as usize - fraction_part.len())
49 ));
50}
51
52pub fn to_human(num: u128, base_unit: &str, maginitude: i8, adjust_magnitude: i8) -> String {
53 let nomination = u128::pow(10, maginitude as u32);
54 let quotient = num / nomination;
55 let remainder = num % nomination;
56
57 if quotient > 0 {
58 let int = quotient.to_formatted_string(&Locale::en);
59 let remainder_str = remainder.to_string();
60 let fraction = if remainder == 0 {
61 "".to_string()
62 } else {
63 let pad = (maginitude as isize - remainder_str.len() as isize).max(0) as usize;
64 format!(".{}{}", "0".repeat(pad), remainder_str)
65 .trim_end_matches('0')
66 .to_string()
67 };
68 let prefix = from_magnitude(adjust_magnitude).unwrap();
69 return format!("{}{} {}{}", int, fraction, prefix, base_unit);
70 }
71
72 to_human(num, base_unit, maginitude - 3, adjust_magnitude - 3)
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 #[test]
79 fn it_works() {
80 assert_eq!(get_match("y").unwrap(), 19);
81 assert_eq!(get_magnitude("y"), -24);
82 assert_eq!(get_match("yocto").unwrap(), 19);
83 assert_eq!(get_magnitude("yocto"), -24);
84 assert_eq!(get_magnitude("1yocto"), -24);
85 }
86
87 #[test]
88 fn parse_test() {
89 assert_eq!(parse("1000m", 24).unwrap(), "1000000000000000000000000");
90 }
91}