Skip to main content

fastnum/decimal/errors/
parse.rs

1use core::{
2    fmt,
3    fmt::{Debug, Display, Formatter},
4    num::{IntErrorKind, ParseIntError},
5};
6
7#[cfg(not(feature = "std"))]
8use alloc::{format, string::String};
9
10use crate::utils::err_prefix;
11
12/// Enum to store the various types of errors that can cause parsing decimal to
13/// fail.
14///
15/// # Example
16///
17/// ```
18/// use fastnum::decimal::Context;
19/// use fastnum::UD256;
20///
21/// if let Err(e) = UD256::from_str("e12", Context::default()) {
22///     println!("Failed conversion to Decimal: {e}");
23/// }
24/// ```
25#[derive(Copy, Clone, PartialEq)]
26pub enum ParseError {
27    /// Value being parsed is empty.
28    ///
29    /// This variant will be constructed when parsing an empty string.
30    Empty,
31
32    /// Contains an invalid digit in its context.
33    ///
34    /// Among other causes, this variant will be constructed when parsing a
35    /// string that contains a non-ASCII char.
36    ///
37    /// This variant is also constructed when a `+` or `-` is misplaced within a
38    /// string either on its own or in the middle of a number.
39    InvalidLiteral,
40
41    /// The number is too large to store in target decimal type.
42    PosOverflow,
43
44    /// The number is too small to store in target decimal type.
45    NegOverflow,
46
47    /// Exponent is too large to store in decimal type.
48    ExponentOverflow,
49
50    /// Value was Signed
51    ///
52    /// This variant will be emitted when the parsing string has a sign literal,
53    /// which would be illegal for unsigned types.
54    Signed,
55
56    /// Invalid radix.
57    InvalidRadix,
58
59    /// Unknown error
60    Unknown,
61}
62
63impl ParseError {
64    #[inline(always)]
65    pub(crate) const fn description(&self) -> &str {
66        use ParseError::*;
67        match self {
68            Empty => "cannot parse decimal from empty string",
69            InvalidLiteral => "invalid literal found in string",
70            PosOverflow => "number too large to fit in target type",
71            NegOverflow => "number too small to fit in target type",
72            Signed => "number would be signed for unsigned type",
73            InvalidRadix => "radix for decimal must be 10",
74            ExponentOverflow => "exponent is too large to fit in target type",
75            Unknown => "unknown error",
76        }
77    }
78
79    #[inline(always)]
80    pub(crate) const fn from_int_error_kind(e: &IntErrorKind) -> ParseError {
81        match e {
82            IntErrorKind::Empty => ParseError::Empty,
83            IntErrorKind::InvalidDigit => ParseError::InvalidLiteral,
84            IntErrorKind::PosOverflow => ParseError::PosOverflow,
85            IntErrorKind::NegOverflow => ParseError::NegOverflow,
86            _ => ParseError::Unknown,
87        }
88    }
89}
90
91impl Display for ParseError {
92    #[inline]
93    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
94        write!(f, "{} {}", err_prefix!(), self.description())
95    }
96}
97
98impl Debug for ParseError {
99    #[inline]
100    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
101        Display::fmt(&self, f)
102    }
103}
104
105impl From<ParseIntError> for ParseError {
106    #[inline]
107    fn from(e: ParseIntError) -> ParseError {
108        Self::from_int_error_kind(e.kind())
109    }
110}
111
112impl core::error::Error for ParseError {
113    #[inline]
114    fn description(&self) -> &str {
115        self.description()
116    }
117}
118
119#[allow(dead_code)]
120#[inline]
121pub(crate) fn pretty_error_msg(ty: &str, e: ParseError) -> String {
122    use ParseError::*;
123    let msg = match e {
124        Empty => "cannot be constructed from an empty string",
125        InvalidLiteral => "string contains invalid characters",
126        PosOverflow => "overflow",
127        NegOverflow => "negative overflow",
128        Signed => "does not support negative values",
129        InvalidRadix => "radix MUST be 10",
130        ExponentOverflow => "exponent overflow",
131        Unknown => "decimal unknown error",
132    };
133
134    format!("{} {ty} {msg}", err_prefix!())
135}