1use alloc::string::String;
6use core::fmt;
7use core::str::FromStr;
8
9use internals::write_err;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
21#[non_exhaustive]
22pub struct ParseIntError {
23 pub(crate) input: String,
24 bits: u8,
26 is_signed: bool,
30 pub(crate) source: core::num::ParseIntError,
31}
32
33impl ParseIntError {
34 pub fn input(&self) -> &str { &self.input }
36}
37
38impl fmt::Display for ParseIntError {
39 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40 let signed = if self.is_signed { "signed" } else { "unsigned" };
41 let n = if self.bits == 8 { "n" } else { "" };
42 write_err!(f, "failed to parse '{}' as a{} {}-bit {} integer", self.input, n, self.bits, signed; self.source)
43 }
44}
45
46#[cfg(feature = "std")]
47impl std::error::Error for ParseIntError {
48 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.source) }
49}
50
51impl From<ParseIntError> for core::num::ParseIntError {
52 fn from(value: ParseIntError) -> Self { value.source }
53}
54
55impl AsRef<core::num::ParseIntError> for ParseIntError {
56 fn as_ref(&self) -> &core::num::ParseIntError { &self.source }
57}
58
59pub trait Integer: FromStr<Err = core::num::ParseIntError> + TryFrom<i8> + Sized {}
62
63macro_rules! impl_integer {
64 ($($type:ty),* $(,)?) => {
65 $(
66 impl Integer for $type {}
67 )*
68 }
69}
70
71impl_integer!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128);
72
73pub fn int<T: Integer, S: AsRef<str> + Into<String>>(s: S) -> Result<T, ParseIntError> {
78 s.as_ref().parse().map_err(|error| {
79 ParseIntError {
80 input: s.into(),
81 bits: u8::try_from(core::mem::size_of::<T>() * 8).expect("max is 128 bits for u128"),
82 is_signed: T::try_from(-1i8).is_ok(),
86 source: error,
87 }
88 })
89}
90
91#[macro_export]
94macro_rules! impl_tryfrom_str_from_int_infallible {
95 ($($from:ty, $to:ident, $inner:ident, $fn:ident);*) => {
96 $(
97 impl core::convert::TryFrom<$from> for $to {
98 type Error = $crate::parse::ParseIntError;
99
100 fn try_from(s: $from) -> core::result::Result<Self, Self::Error> {
101 $crate::parse::int::<$inner, $from>(s).map($to::$fn)
102 }
103 }
104 )*
105 }
106}
107
108#[macro_export]
113macro_rules! impl_parse_str_from_int_infallible {
114 ($to:ident, $inner:ident, $fn:ident) => {
115 #[cfg(all(feature = "alloc", not(feature = "std")))]
116 $crate::impl_tryfrom_str_from_int_infallible!(&str, $to, $inner, $fn; alloc::string::String, $to, $inner, $fn; alloc::boxed::Box<str>, $to, $inner, $fn);
117 #[cfg(feature = "std")]
118 $crate::impl_tryfrom_str_from_int_infallible!(&str, $to, $inner, $fn; std::string::String, $to, $inner, $fn; std::boxed::Box<str>, $to, $inner, $fn);
119
120 impl core::str::FromStr for $to {
121 type Err = $crate::parse::ParseIntError;
122
123 fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
124 $crate::parse::int::<$inner, &str>(s).map($to::$fn)
125 }
126 }
127
128 }
129}
130
131#[macro_export]
133macro_rules! impl_tryfrom_str {
134 ($($from:ty, $to:ty, $err:ty, $inner_fn:expr);*) => {
135 $(
136 impl core::convert::TryFrom<$from> for $to {
137 type Error = $err;
138
139 fn try_from(s: $from) -> core::result::Result<Self, Self::Error> {
140 $inner_fn(s)
141 }
142 }
143 )*
144 }
145}
146
147#[macro_export]
149macro_rules! impl_parse_str {
150 ($to:ty, $err:ty, $inner_fn:expr) => {
151 $crate::impl_tryfrom_str!(&str, $to, $err, $inner_fn; String, $to, $err, $inner_fn; Box<str>, $to, $err, $inner_fn);
152
153 impl core::str::FromStr for $to {
154 type Err = $err;
155
156 fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
157 $inner_fn(s)
158 }
159 }
160 }
161}
162
163pub fn hex_remove_prefix(s: &str) -> Result<&str, PrefixedHexError> {
169 if let Some(checked) = s.strip_prefix("0x") {
170 Ok(checked)
171 } else if let Some(checked) = s.strip_prefix("0X") {
172 Ok(checked)
173 } else {
174 Err(MissingPrefixError::new(s.into()).into())
175 }
176}
177
178pub fn hex_check_unprefixed(s: &str) -> Result<&str, UnprefixedHexError> {
184 if s.starts_with("0x") || s.starts_with("0X") {
185 return Err(ContainsPrefixError::new(s.into()).into());
186 }
187 Ok(s)
188}
189
190pub fn hex_u32(s: &str) -> Result<u32, ParseIntError> {
198 let unchecked = hex_remove_optional_prefix(s);
199 Ok(hex_u32_unchecked(unchecked)?)
200}
201
202pub fn hex_u32_prefixed(s: &str) -> Result<u32, PrefixedHexError> {
209 let checked = hex_remove_prefix(s)?;
210 Ok(hex_u32_unchecked(checked)?)
211}
212
213pub fn hex_u32_unprefixed(s: &str) -> Result<u32, UnprefixedHexError> {
220 let checked = hex_check_unprefixed(s)?;
221 Ok(hex_u32_unchecked(checked)?)
222}
223
224pub fn hex_u32_unchecked(s: &str) -> Result<u32, ParseIntError> {
231 u32::from_str_radix(s, 16).map_err(|error| ParseIntError {
232 input: s.into(),
233 bits: 32,
234 is_signed: false,
235 source: error,
236 })
237}
238
239pub fn hex_u128(s: &str) -> Result<u128, ParseIntError> {
247 let unchecked = hex_remove_optional_prefix(s);
248 Ok(hex_u128_unchecked(unchecked)?)
249}
250
251pub fn hex_u128_prefixed(s: &str) -> Result<u128, PrefixedHexError> {
258 let checked = hex_remove_prefix(s)?;
259 Ok(hex_u128_unchecked(checked)?)
260}
261
262pub fn hex_u128_unprefixed(s: &str) -> Result<u128, UnprefixedHexError> {
269 let checked = hex_check_unprefixed(s)?;
270 Ok(hex_u128_unchecked(checked)?)
271}
272
273pub fn hex_u128_unchecked(s: &str) -> Result<u128, ParseIntError> {
280 u128::from_str_radix(s, 16).map_err(|error| ParseIntError {
281 input: s.into(),
282 bits: 128,
283 is_signed: false,
284 source: error,
285 })
286}
287
288pub(crate) fn hex_remove_optional_prefix(s: &str) -> &str {
290 if let Some(stripped) = s.strip_prefix("0x") {
291 stripped
292 } else if let Some(stripped) = s.strip_prefix("0X") {
293 stripped
294 } else {
295 s
296 }
297}
298
299#[derive(Debug, Clone, Eq, PartialEq)]
301pub enum PrefixedHexError {
302 MissingPrefix(MissingPrefixError),
304 ParseInt(ParseIntError),
306}
307
308impl fmt::Display for PrefixedHexError {
309 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
310 use PrefixedHexError::*;
311
312 match *self {
313 MissingPrefix(ref e) => write_err!(f, "hex string is missing prefix"; e),
314 ParseInt(ref e) => write_err!(f, "prefixed hex string invalid int"; e),
315 }
316 }
317}
318
319#[cfg(feature = "std")]
320impl std::error::Error for PrefixedHexError {
321 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
322 use PrefixedHexError::*;
323
324 match *self {
325 MissingPrefix(ref e) => Some(e),
326 ParseInt(ref e) => Some(e),
327 }
328 }
329}
330
331impl From<MissingPrefixError> for PrefixedHexError {
332 fn from(e: MissingPrefixError) -> Self { Self::MissingPrefix(e) }
333}
334
335impl From<ParseIntError> for PrefixedHexError {
336 fn from(e: ParseIntError) -> Self { Self::ParseInt(e) }
337}
338
339#[derive(Debug, Clone, Eq, PartialEq)]
341pub enum UnprefixedHexError {
342 ContainsPrefix(ContainsPrefixError),
344 ParseInt(ParseIntError),
346}
347
348impl fmt::Display for UnprefixedHexError {
349 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
350 use UnprefixedHexError::*;
351
352 match *self {
353 ContainsPrefix(ref e) => write_err!(f, "hex string is contains prefix"; e),
354 ParseInt(ref e) => write_err!(f, "hex string parse int"; e),
355 }
356 }
357}
358
359#[cfg(feature = "std")]
360impl std::error::Error for UnprefixedHexError {
361 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
362 use UnprefixedHexError::*;
363
364 match *self {
365 ContainsPrefix(ref e) => Some(e),
366 ParseInt(ref e) => Some(e),
367 }
368 }
369}
370
371impl From<ContainsPrefixError> for UnprefixedHexError {
372 fn from(e: ContainsPrefixError) -> Self { Self::ContainsPrefix(e) }
373}
374
375impl From<ParseIntError> for UnprefixedHexError {
376 fn from(e: ParseIntError) -> Self { Self::ParseInt(e) }
377}
378
379#[derive(Debug, Clone, Eq, PartialEq)]
381pub struct MissingPrefixError {
382 hex: String,
383}
384
385impl MissingPrefixError {
386 pub(crate) fn new(hex: String) -> Self { Self { hex } }
388}
389
390impl fmt::Display for MissingPrefixError {
391 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
392 write!(f, "hex string is missing a prefix (e.g. 0x): {}", self.hex)
393 }
394}
395
396#[cfg(feature = "std")]
397impl std::error::Error for MissingPrefixError {}
398
399#[derive(Debug, Clone, Eq, PartialEq)]
401pub struct ContainsPrefixError {
402 hex: String,
403}
404
405impl ContainsPrefixError {
406 pub(crate) fn new(hex: String) -> Self { Self { hex } }
408}
409
410impl fmt::Display for ContainsPrefixError {
411 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
412 write!(f, "hex string contains a prefix: {}", self.hex)
413 }
414}
415
416#[cfg(feature = "std")]
417impl std::error::Error for ContainsPrefixError {}
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422
423 #[test]
424 fn parse_u32_from_hex_prefixed() {
425 let want = 171;
426 let got = hex_u32("0xab").expect("failed to parse prefixed hex");
427 assert_eq!(got, want);
428 }
429
430 #[test]
431 fn parse_u32_from_hex_no_prefix() {
432 let want = 171;
433 let got = hex_u32("ab").expect("failed to parse non-prefixed hex");
434 assert_eq!(got, want);
435 }
436
437 #[test]
438 fn parse_u128_from_hex_prefixed() {
439 let want = 3735928559;
440 let got = hex_u128("0xdeadbeef").expect("failed to parse prefixed hex");
441 assert_eq!(got, want);
442 }
443
444 #[test]
445 fn parse_u128_from_hex_no_prefix() {
446 let want = 3735928559;
447 let got = hex_u128("deadbeef").expect("failed to parse non-prefixed hex");
448 assert_eq!(got, want);
449 }
450
451 #[test]
452 fn parse_u32_from_hex_unchecked_errors_on_prefix() {
453 assert!(hex_u32_unchecked("0xab").is_err());
454 }
455}