1pub(crate) fn parse_decimal_number(
11 s: &str,
12 pref_const: u128,
13) -> Result<u128, DecimalNumberParsingError> {
14 let (int, fract) = if let Some((whole, fractional)) = s.trim().split_once('.') {
15 let int: u128 = whole
16 .parse()
17 .map_err(|_| DecimalNumberParsingError::InvalidNumber(s.to_owned()))?;
18 let mut fract: u128 = fractional
19 .parse()
20 .map_err(|_| DecimalNumberParsingError::InvalidNumber(s.to_owned()))?;
21 let len = u32::try_from(fractional.len())
22 .map_err(|_| DecimalNumberParsingError::InvalidNumber(s.to_owned()))?;
23 fract = fract
24 .checked_mul(
25 pref_const
26 .checked_div(10u128.checked_pow(len).ok_or_else(|| {
27 DecimalNumberParsingError::LongFractional(fractional.to_owned())
28 })?)
29 .filter(|n| *n != 0u128)
30 .ok_or_else(|| {
31 DecimalNumberParsingError::LongFractional(fractional.to_owned())
32 })?,
33 )
34 .ok_or_else(|| DecimalNumberParsingError::LongFractional(fractional.to_owned()))?;
35 (int, fract)
36 } else {
37 let int: u128 = s
38 .parse()
39 .map_err(|_| DecimalNumberParsingError::InvalidNumber(s.to_owned()))?;
40 (int, 0)
41 };
42 let result = fract
43 .checked_add(
44 int.checked_mul(pref_const)
45 .ok_or_else(|| DecimalNumberParsingError::LongWhole(int.to_string()))?,
46 )
47 .ok_or_else(|| DecimalNumberParsingError::LongWhole(int.to_string()))?;
48 Ok(result)
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub enum DecimalNumberParsingError {
53 InvalidNumber(String),
54 LongWhole(String),
55 LongFractional(String),
56}
57
58impl std::error::Error for DecimalNumberParsingError {}
59
60impl std::fmt::Display for DecimalNumberParsingError {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 match self {
63 DecimalNumberParsingError::InvalidNumber(s) => {
64 write!(f, "invalid number: {}", s)
65 }
66 DecimalNumberParsingError::LongWhole(s) => {
67 write!(f, "too long whole part: {}", s)
68 }
69 DecimalNumberParsingError::LongFractional(s) => {
70 write!(f, "too long fractional part: {}", s)
71 }
72 }
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 const TEST: [(u128, &str, u128); 6] = [
81 (129_380_000_001_u128, "129.380000001", 10u128.pow(9)),
82 (
83 12_938_000_000_100_000_000_u128,
84 "12938000000.1",
85 10u128.pow(9),
86 ),
87 (129_380_000_001_u128, "0.129380000001", 10u128.pow(12)),
88 (129_380_000_001_000_u128, "129.380000001000", 10u128.pow(12)),
89 (
90 9_488_129_380_000_001_u128,
91 "9488.129380000001",
92 10u128.pow(12),
93 ),
94 (129_380_000_001_u128, "00.129380000001", 10u128.pow(12)),
95 ];
96
97 #[test]
98 fn parse_test() {
99 for (expected_value, str_value, precision) in TEST {
100 let parsed_value = parse_decimal_number(str_value, precision).unwrap();
101 assert_eq!(parsed_value, expected_value)
102 }
103 }
104
105 #[test]
106 fn test_long_fract() {
107 let data = "1.23456";
108 let prefix = 10000u128;
109 assert_eq!(
110 parse_decimal_number(data, prefix),
111 Err(DecimalNumberParsingError::LongFractional(23456.to_string()))
112 );
113 }
114
115 #[test]
116 fn invalidnumber_whole() {
117 let num = "1h4.7859";
118 let prefix: u128 = 10000;
119 assert_eq!(
120 parse_decimal_number(num, prefix),
121 Err(DecimalNumberParsingError::InvalidNumber(
122 "1h4.7859".to_owned()
123 ))
124 );
125 }
126 #[test]
127 fn invalidnumber_fract() {
128 let num = "14.785h9";
129 let prefix: u128 = 10000;
130 assert_eq!(
131 parse_decimal_number(num, prefix),
132 Err(DecimalNumberParsingError::InvalidNumber(
133 "14.785h9".to_owned()
134 ))
135 );
136 }
137
138 #[test]
139 fn max_long_fract() {
140 let max_data = 10u128.pow(17) + 1;
141 let data = "1.".to_string() + max_data.to_string().as_str();
142 let prefix = 10u128.pow(17);
143 assert_eq!(
144 parse_decimal_number(data.as_str(), prefix),
145 Err(DecimalNumberParsingError::LongFractional(
146 max_data.to_string()
147 ))
148 );
149 }
150
151 #[test]
152 fn parse_u128_errortest() {
153 let test_data = u128::MAX.to_string();
154 let gas = parse_decimal_number(&test_data, 10u128.pow(9));
155 assert_eq!(
156 gas,
157 Err(DecimalNumberParsingError::LongWhole(u128::MAX.to_string()))
158 );
159 }
160
161 #[test]
162 fn test() {
163 let data = "1.000000000000000000000000000000000000001";
164 let prefix = 100u128;
165 assert_eq!(
166 parse_decimal_number(data, prefix),
167 Err(DecimalNumberParsingError::LongFractional(
168 "000000000000000000000000000000000000001".to_string()
169 ))
170 );
171 }
172}