unc_token/
utils.rs

1/// Parsing decimal numbers from `&str` type in `u128`.
2/// Function also takes a value of metric prefix in u128 type.
3/// `parse_str` use the `u128` type, and have the same max and min values.
4///
5/// If the fractional part is longer than several zeros in the prefix, it will return the error `DecimalNumberParsingError::LongFractional`.
6///
7/// If the string slice has invalid chars, it will return the error `DecimalNumberParsingError::InvalidNumber`.
8///
9/// If the whole part of the number has a value more than the `u64` maximum value, it will return the error `DecimalNumberParsingError::LongWhole`.
10pub(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}