1pub(crate) fn parse_decimal_number(
11 s: &str,
12 pref_const: u64,
13) -> Result<u64, DecimalNumberParsingError> {
14 let (int, fract) = if let Some((whole, fractional)) = s.trim().split_once('.') {
15 let int: u64 = whole
16 .parse()
17 .map_err(|_| DecimalNumberParsingError::InvalidNumber(s.to_owned()))?;
18 let mut fract: u64 = 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(10u64.checked_pow(len).ok_or_else(|| {
27 DecimalNumberParsingError::LongFractional(fractional.to_owned())
28 })?)
29 .filter(|n| *n != 0u64)
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: u64 = 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 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
60 None
61 }
62
63 fn description(&self) -> &str {
64 "description() is deprecated; use Display"
65 }
66
67 fn cause(&self) -> Option<&dyn std::error::Error> {
68 self.source()
69 }
70}
71
72impl std::fmt::Display for DecimalNumberParsingError {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 match self {
75 DecimalNumberParsingError::InvalidNumber(s) => {
76 write!(f, "Invalid number: {}", s)
77 }
78 DecimalNumberParsingError::LongWhole(s) => {
79 write!(f, "Long whole part: {}", s)
80 }
81 DecimalNumberParsingError::LongFractional(s) => {
82 write!(f, "Long fractional part: {}", s)
83 }
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 const TEST: [(u64, &str, u64); 6] = [
93 (129_380_000_001_u64, "129.380000001", 10u64.pow(9)),
94 (
95 12_938_000_000_100_000_000_u64,
96 "12938000000.1",
97 10u64.pow(9),
98 ),
99 (129_380_000_001_u64, "0.129380000001", 10u64.pow(12)),
100 (129_380_000_001_000_u64, "129.380000001000", 10u64.pow(12)),
101 (
102 9_488_129_380_000_001_u64,
103 "9488.129380000001",
104 10u64.pow(12),
105 ),
106 (129_380_000_001_u64, "00.129380000001", 10u64.pow(12)),
107 ];
108
109 #[test]
110 fn parse_test() {
111 for (expected_value, str_value, precision) in TEST {
112 let parsed_value = parse_decimal_number(str_value, precision).unwrap();
113 assert_eq!(parsed_value, expected_value)
114 }
115 }
116
117 #[test]
118 fn test_long_fract() {
119 let data = "1.23456";
120 let prefix = 10000u64;
121 assert_eq!(
122 parse_decimal_number(data, prefix),
123 Err(DecimalNumberParsingError::LongFractional(23456.to_string()))
124 );
125 }
126
127 #[test]
128 fn invalidnumber_whole() {
129 let num = "1h4.7859";
130 let prefix: u64 = 10000;
131 assert_eq!(
132 parse_decimal_number(num, prefix),
133 Err(DecimalNumberParsingError::InvalidNumber(
134 "1h4.7859".to_owned()
135 ))
136 );
137 }
138 #[test]
139 fn invalidnumber_fract() {
140 let num = "14.785h9";
141 let prefix: u64 = 10000;
142 assert_eq!(
143 parse_decimal_number(num, prefix),
144 Err(DecimalNumberParsingError::InvalidNumber(
145 "14.785h9".to_owned()
146 ))
147 );
148 }
149
150 #[test]
151 fn max_long_fract() {
152 let max_data = 10u64.pow(17) + 1;
153 let data = "1.".to_string() + max_data.to_string().as_str();
154 let prefix = 10u64.pow(17);
155 assert_eq!(
156 parse_decimal_number(data.as_str(), prefix),
157 Err(DecimalNumberParsingError::LongFractional(
158 max_data.to_string()
159 ))
160 );
161 }
162
163 #[test]
164 fn long_whole_test() {
165 let data = 10u64.pow(17) + 1;
166 let prefix = 10u64.pow(12);
167 let s = data.to_string() + "." + "1";
168 assert_eq!(
169 parse_decimal_number(s.as_str(), prefix),
170 Err(DecimalNumberParsingError::LongWhole(data.to_string()))
171 );
172 }
173
174 #[test]
175 fn parse_u64_errortest() {
176 let test_data = u64::MAX.to_string();
177 let gas = parse_decimal_number(&test_data, 10u64.pow(9));
178 assert_eq!(
179 gas,
180 Err(DecimalNumberParsingError::LongWhole(u64::MAX.to_string()))
181 );
182 }
183
184 #[test]
185 fn test() {
186 let data = "1.000000000000000000000000000000000000001";
187 let prefix = 100u64;
188 assert_eq!(
189 parse_decimal_number(data, prefix),
190 Err(DecimalNumberParsingError::LongFractional(
191 "000000000000000000000000000000000000001".to_string()
192 ))
193 );
194 }
195}