Skip to main content

lean_decimal/
from_str.rs

1use core::fmt;
2use core::str::FromStr;
3
4use crate::{Decimal, UnderlyingInt};
5
6/// Error in converting from string.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ParseError {
9    /// Empty string.
10    Empty,
11    /// Invalid digit in the string.
12    Invalid,
13    /// Overflow.
14    Overflow,
15    /// Precision out of range.
16    Precision,
17}
18
19impl fmt::Display for ParseError {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        let s = match self {
22            Self::Empty => "empty string",
23            Self::Invalid => "invalid digit in the string",
24            Self::Overflow => "overflow",
25            Self::Precision => "precision out of range",
26        };
27        write!(f, "{s}")
28    }
29}
30
31impl core::error::Error for ParseError {}
32
33use core::num::{IntErrorKind, ParseIntError};
34impl From<ParseIntError> for ParseError {
35    fn from(pie: ParseIntError) -> Self {
36        match pie.kind() {
37            IntErrorKind::Empty => ParseError::Empty,
38            IntErrorKind::InvalidDigit => ParseError::Invalid,
39            _ => ParseError::Overflow,
40        }
41    }
42}
43
44impl<I> FromStr for Decimal<I>
45where
46    I: UnderlyingInt + FromStr<Err = ParseIntError>,
47{
48    type Err = ParseError;
49    fn from_str(s: &str) -> Result<Self, ParseError> {
50        if s.is_empty() {
51            return Err(ParseError::Empty);
52        }
53
54        let (s, sign) = match s.chars().next() {
55            Some('-') => (&s[1..], 1),
56            Some('+') => (&s[1..], 0),
57            _ => (s, 0),
58        };
59
60        let (man, scale) = if let Some((int_str, frac_str)) = s.split_once('.') {
61            let scale = frac_str.len() as u32;
62            if scale > I::MAX_SCALE {
63                return Err(ParseError::Precision);
64            }
65
66            let int_num = I::from_str(int_str)?;
67            let frac_num = I::from_str(frac_str)?;
68
69            let int_part = int_num.checked_mul_exp(scale).ok_or(ParseError::Overflow)?;
70            if int_part > I::MAX_MATISSA {
71                return Err(ParseError::Overflow);
72            }
73
74            (int_part + frac_num, scale)
75        } else {
76            (I::from_str(s)?, 0)
77        };
78
79        if man > I::MAX_MATISSA {
80            return Err(ParseError::Overflow);
81        }
82        Ok(Self::pack(sign, scale, man))
83    }
84}