1use core::fmt;
2use core::str::FromStr;
3
4use crate::{Decimal, UnderlyingInt};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ParseError {
9 Empty,
11 Invalid,
13 Overflow,
15 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}