1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use core::fmt::{self, Display, Formatter};
#[cfg(feature = "std")]
use std::error::Error;

pub trait FromStrRadix: Sized {
    fn from_str_radix(s: &str, radix: u32) -> Result<Self, ParseError>;
}

macro_rules! from_str_radix_impl {
    ($($ty:ident)*) => { $(
        impl FromStrRadix for $ty {
            fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseError> {
                assert!(
                    (2..=36).contains(&radix),
                    "from_str_radix: radix must lie in the range `[2, 36]` - found {}",
                    radix,
                );

                let src = src.as_bytes();

                let (positive, digits) = match *src {
                    [b'+', ref digits @ ..] => (true, digits),
                    [b'-', ref digits @ ..] => (false, digits),
                    ref digits => (true, digits),
                };

                if digits.is_empty() {
                    return Err(ParseError {
                        kind: ParseErrorKind::NoDigits,
                    });
                }

                let overflow_kind = if positive {
                    ParseErrorKind::AboveMax
                } else {
                    ParseErrorKind::BelowMin
                };

                let mut result: Self = 0;

                for &digit in digits {
                    let digit_value =
                        char::from(digit)
                            .to_digit(radix)
                            .ok_or_else(|| ParseError {
                                kind: ParseErrorKind::InvalidDigit,
                            })?;

                    result = result
                        .checked_mul(radix as Self)
                        .ok_or_else(|| ParseError {
                            kind: overflow_kind,
                        })?;

                    result = if positive {
                        result.checked_add(digit_value as Self)
                    } else {
                        result.checked_sub(digit_value as Self)
                    }
                    .ok_or_else(|| ParseError {
                        kind: overflow_kind,
                    })?;
                }

                Ok(result)
            }
        }
    )* }
}
from_str_radix_impl! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize }

/// An error which can be returned when parsing a bounded integer.
///
/// This is the error type of all bounded integers' `from_str_radix()` functions (such as
/// [`BoundedI8::from_str_radix`](crate::BoundedI8::from_str_radix)) as well as their
/// [`FromStr`](std::str::FromStr) implementations.
#[derive(Debug, Clone)]
pub struct ParseError {
    kind: ParseErrorKind,
}

impl ParseError {
    /// Gives the cause of the error.
    #[must_use]
    pub fn kind(&self) -> ParseErrorKind {
        self.kind
    }
}

impl Display for ParseError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self.kind() {
            ParseErrorKind::NoDigits => f.write_str("no digits found"),
            ParseErrorKind::InvalidDigit => f.write_str("invalid digit found in string"),
            ParseErrorKind::AboveMax => f.write_str("number too high to fit in target range"),
            ParseErrorKind::BelowMin => f.write_str("number too low to fit in target range"),
        }
    }
}

#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
impl Error for ParseError {}

/// The cause of the failure to parse the integer.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ParseErrorKind {
    /// No digits were found in the input string.
    ///
    /// This happens when the input is an empty string, or when it only contains a `+` or `-`.
    #[non_exhaustive]
    NoDigits,
    /// An invalid digit was found in the input.
    #[non_exhaustive]
    InvalidDigit,
    /// The integer is too high to fit in the bounded integer's range.
    #[non_exhaustive]
    AboveMax,
    /// The integer is too low to fit in the bounded integer's range.
    #[non_exhaustive]
    BelowMin,
}

pub fn error_below_min() -> ParseError {
    ParseError {
        kind: ParseErrorKind::BelowMin,
    }
}
pub fn error_above_max() -> ParseError {
    ParseError {
        kind: ParseErrorKind::AboveMax,
    }
}