1use core::error::Error;
2use core::fmt::{self, Display, Formatter};
3
4macro_rules! from_str_radix_impl {
5 ($($ty:ident)*) => { $(
6 impl $crate::__private::Dispatch<$ty> {
7 pub const fn from_ascii_radix(src: &[u8], radix: u32) -> Result<$ty, ParseError> {
10 assert!(
11 2 <= radix && radix <= 36,
12 "from_str_radix: radix must lie in the range `[2, 36]`",
13 );
14
15 macro_rules! yeet {
16 ($e:expr) => { return Err(ParseError { kind: $e }) };
17 }
18
19 let (positive, digits) = match *src {
20 [b'+', ref digits @ ..] => (true, digits),
21 [b'-', ref digits @ ..] => (false, digits),
22 ref digits => (true, digits),
23 };
24
25 if digits.is_empty() {
26 yeet!(ParseErrorKind::NoDigits);
27 }
28
29 let overflow_kind = if positive {
30 ParseErrorKind::AboveMax
31 } else {
32 ParseErrorKind::BelowMin
33 };
34
35 let mut result: $ty = 0;
36
37 let mut i = 0;
38 while i < digits.len() {
39 let digit = digits[i];
40
41 let Some(digit_value) = (digit as char).to_digit(radix) else {
42 yeet!(ParseErrorKind::InvalidDigit);
43 };
44
45 #[allow(clippy::cast_possible_wrap)]
46 #[allow(clippy::cast_possible_truncation)]
47 let Some(new_result) = result.checked_mul(radix as $ty) else {
48 yeet!(overflow_kind);
49 };
50
51 #[allow(clippy::cast_possible_wrap)]
52 #[allow(clippy::cast_possible_truncation)]
53 let Some(new_result) = (if positive {
54 new_result.checked_add(digit_value as $ty)
55 } else {
56 new_result.checked_sub(digit_value as $ty)
57 }) else {
58 yeet!(overflow_kind);
59 };
60
61 result = new_result;
62
63 i += 1;
64 }
65
66 Ok(result)
67 }
68 }
69 )* }
70}
71from_str_radix_impl! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize }
72
73#[derive(Debug, Clone)]
79pub struct ParseError {
80 kind: ParseErrorKind,
81}
82
83impl ParseError {
84 #[must_use]
86 pub fn kind(&self) -> ParseErrorKind {
87 self.kind
88 }
89}
90
91impl Display for ParseError {
92 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
93 match self.kind() {
94 ParseErrorKind::NoDigits => f.write_str("no digits found"),
95 ParseErrorKind::InvalidDigit => f.write_str("invalid digit found in string"),
96 ParseErrorKind::AboveMax => f.write_str("number too high to fit in target range"),
97 ParseErrorKind::BelowMin => f.write_str("number too low to fit in target range"),
98 }
99 }
100}
101
102impl Error for ParseError {}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106#[non_exhaustive]
107pub enum ParseErrorKind {
108 #[non_exhaustive]
112 NoDigits,
113 #[non_exhaustive]
115 InvalidDigit,
116 #[non_exhaustive]
118 AboveMax,
119 #[non_exhaustive]
121 BelowMin,
122}
123
124#[must_use]
125pub const fn error_below_min() -> ParseError {
126 ParseError {
127 kind: ParseErrorKind::BelowMin,
128 }
129}
130#[must_use]
131pub const fn error_above_max() -> ParseError {
132 ParseError {
133 kind: ParseErrorKind::AboveMax,
134 }
135}