1use std::str::FromStr;
20
21use crate::{Error, Result};
22
23pub fn decode_base10(amount: &str, decimal_places: usize, strict: bool) -> Result<u64> {
24 let mut s: Vec<char> = amount.to_string().chars().collect();
25
26 let point: usize = if let Some(p) = amount.find('.') {
28 s.remove(p);
29 p
30 } else {
31 s.len()
32 };
33
34 for i in &s {
36 if !i.is_ascii_digit() {
37 return Err(Error::ParseFailed("Found non-digits"))
38 }
39 }
40
41 let actual_places = s.len() - point;
43 if actual_places < decimal_places {
44 s.extend(vec!['0'; decimal_places - actual_places])
45 }
46
47 let mut round = false;
49 if actual_places > decimal_places {
50 let end = point + decimal_places;
51 for i in &s[end..s.len()] {
52 if *i != '0' {
53 round = true;
54 break
55 }
56 }
57 s.truncate(end);
58 }
59
60 if strict && round {
61 return Err(Error::ParseFailed("Would end up rounding while strict"))
62 }
63
64 let number = u64::from_str(&String::from_iter(&s))?;
66
67 if round && number == u64::MAX {
69 return Err(Error::ParseFailed("u64 overflow"))
70 }
71
72 Ok(number + round as u64)
73}
74
75pub fn encode_base10(amount: u64, decimal_places: usize) -> String {
76 let mut s: Vec<char> =
77 format!("{:0width$}", amount, width = 1 + decimal_places).chars().collect();
78 s.insert(s.len() - decimal_places, '.');
79
80 String::from_iter(&s).trim_end_matches('0').trim_end_matches('.').to_string()
81}
82
83#[cfg(test)]
84mod tests {
85 use super::{decode_base10, encode_base10};
86
87 #[test]
88 fn test_decode_base10() {
89 assert_eq!(124, decode_base10("12.33", 1, false).unwrap());
90 assert_eq!(1233000, decode_base10("12.33", 5, false).unwrap());
91 assert_eq!(1200000, decode_base10("12.", 5, false).unwrap());
92 assert_eq!(1200000, decode_base10("12", 5, false).unwrap());
93 assert!(decode_base10("12.33", 1, true).is_err());
94 }
95
96 #[test]
97 fn test_encode_base10() {
98 assert_eq!("23.4321111", &encode_base10(234321111, 7));
99 assert_eq!("23432111.1", &encode_base10(234321111, 1));
100 assert_eq!("234321.1", &encode_base10(2343211, 1));
101 assert_eq!("2343211", &encode_base10(2343211, 0));
102 assert_eq!("0.00002343", &encode_base10(2343, 8));
103 }
104}