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, &'static str, u128); 6] = [
81 (129380_000_001u128, "129.380000001", 10u128.pow(9)),
82 (
83 12938_000_000_100_000_000u128,
84 "12938000000.1",
85 10u128.pow(9),
86 ),
87 (129380_000_001u128, "0.129380000001", 10u128.pow(12)),
88 (129380_000_001_000u128, "129.380000001000", 10u128.pow(12)),
89 (9488129380_000_001u128, "9488.129380000001", 10u128.pow(12)),
90 (129380_000_001u128, "00.129380000001", 10u128.pow(12)),
91 ];
92
93 #[test]
94 fn parse_test() {
95 for (expected_value, str_value, precision) in TEST {
96 let parsed_value = parse_decimal_number(str_value, precision).unwrap();
97 assert_eq!(parsed_value, expected_value)
98 }
99 }
100
101 #[test]
102 fn test_long_fract() {
103 let data = "1.23456";
104 let prefix = 10000u128;
105 assert_eq!(
106 parse_decimal_number(data, prefix),
107 Err(DecimalNumberParsingError::LongFractional(23456.to_string()))
108 );
109 }
110
111 #[test]
112 fn invalidnumber_whole() {
113 let num = "1h4.7859";
114 let prefix: u128 = 10000;
115 assert_eq!(
116 parse_decimal_number(num, prefix),
117 Err(DecimalNumberParsingError::InvalidNumber(
118 "1h4.7859".to_owned()
119 ))
120 );
121 }
122 #[test]
123 fn invalidnumber_fract() {
124 let num = "14.785h9";
125 let prefix: u128 = 10000;
126 assert_eq!(
127 parse_decimal_number(num, prefix),
128 Err(DecimalNumberParsingError::InvalidNumber(
129 "14.785h9".to_owned()
130 ))
131 );
132 }
133
134 #[test]
135 fn max_long_fract() {
136 let max_data = 10u128.pow(17) + 1;
137 let data = "1.".to_string() + max_data.to_string().as_str();
138 let prefix = 10u128.pow(17);
139 assert_eq!(
140 parse_decimal_number(data.as_str(), prefix),
141 Err(DecimalNumberParsingError::LongFractional(
142 max_data.to_string()
143 ))
144 );
145 }
146
147 #[test]
148 fn parse_u128_errortest() {
149 let test_data = u128::MAX.to_string();
150 let gas = parse_decimal_number(&test_data, 10u128.pow(9));
151 assert_eq!(
152 gas,
153 Err(DecimalNumberParsingError::LongWhole(u128::MAX.to_string()))
154 );
155 }
156
157 #[test]
158 fn test() {
159 let data = "1.000000000000000000000000000000000000001";
160 let prefix = 100u128;
161 assert_eq!(
162 parse_decimal_number(data, prefix),
163 Err(DecimalNumberParsingError::LongFractional(
164 "000000000000000000000000000000000000001".to_string()
165 ))
166 );
167 }
168}