1use core::convert::Infallible;
6use core::fmt;
7use core::str::FromStr;
8
9use internals::error::InputString;
10use internals::write_err;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
22#[non_exhaustive]
23pub struct ParseIntError {
24 pub(crate) input: InputString,
25 pub(crate) bits: u8,
27 pub(crate) is_signed: bool,
31 pub(crate) source: core::num::ParseIntError,
32}
33
34impl fmt::Display for ParseIntError {
35 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36 let signed = if self.is_signed { "signed" } else { "unsigned" };
37 write_err!(f, "{} ({}, {}-bit)", self.input.display_cannot_parse("integer"), signed, self.bits; self.source)
38 }
39}
40
41#[cfg(feature = "std")]
42impl std::error::Error for ParseIntError {
43 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.source) }
44}
45
46impl From<ParseIntError> for core::num::ParseIntError {
47 fn from(value: ParseIntError) -> Self { value.source }
48}
49
50impl AsRef<core::num::ParseIntError> for ParseIntError {
51 fn as_ref(&self) -> &core::num::ParseIntError { &self.source }
52}
53
54pub trait Integer:
58 FromStr<Err = core::num::ParseIntError> + TryFrom<i8> + Sized + sealed::Sealed
59{
60}
61
62macro_rules! impl_integer {
63 ($($type:ty),* $(,)?) => {
64 $(
65 impl Integer for $type {}
66 impl sealed::Sealed for $type {}
67 )*
68 }
69}
70
71impl_integer!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128);
72
73mod sealed {
74 pub trait Sealed {}
76}
77
78pub fn int_from_str<T: Integer>(s: &str) -> Result<T, ParseIntError> { int(s) }
95
96#[cfg(feature = "alloc")]
102pub fn int_from_string<T: Integer>(s: alloc::string::String) -> Result<T, ParseIntError> { int(s) }
103
104#[cfg(feature = "alloc")]
110pub fn int_from_box<T: Integer>(s: alloc::boxed::Box<str>) -> Result<T, ParseIntError> { int(s) }
111
112fn int<T: Integer, S: AsRef<str> + Into<InputString>>(s: S) -> Result<T, ParseIntError> {
114 s.as_ref().parse().map_err(|error| {
115 ParseIntError {
116 input: s.into(),
117 bits: u8::try_from(core::mem::size_of::<T>() * 8).expect("max is 128 bits for u128"),
118 is_signed: T::try_from(-1i8).is_ok(),
122 source: error,
123 }
124 })
125}
126
127macro_rules! impl_parse_str_from_int_infallible {
152 ($to:ident, $inner:ident, $fn:ident) => {
153 impl $crate::_export::_core::str::FromStr for $to {
154 type Err = $crate::parse_int::ParseIntError;
155
156 fn from_str(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Err> {
157 $crate::_export::_core::convert::TryFrom::try_from(s)
158 }
159 }
160
161 impl $crate::_export::_core::convert::TryFrom<&str> for $to {
162 type Error = $crate::parse_int::ParseIntError;
163
164 fn try_from(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Error> {
165 $crate::parse_int::int_from_str::<$inner>(s).map($to::$fn)
166 }
167 }
168
169 #[cfg(feature = "alloc")]
170 impl $crate::_export::_core::convert::TryFrom<alloc::string::String> for $to {
171 type Error = $crate::parse_int::ParseIntError;
172
173 fn try_from(
174 s: alloc::string::String,
175 ) -> $crate::_export::_core::result::Result<Self, Self::Error> {
176 $crate::parse_int::int_from_string::<$inner>(s).map($to::$fn)
177 }
178 }
179
180 #[cfg(feature = "alloc")]
181 impl $crate::_export::_core::convert::TryFrom<alloc::boxed::Box<str>> for $to {
182 type Error = $crate::parse_int::ParseIntError;
183
184 fn try_from(
185 s: alloc::boxed::Box<str>,
186 ) -> $crate::_export::_core::result::Result<Self, Self::Error> {
187 $crate::parse_int::int_from_box::<$inner>(s).map($to::$fn)
188 }
189 }
190 };
191}
192pub(crate) use impl_parse_str_from_int_infallible;
193
194macro_rules! impl_parse_str {
216 ($to:ty, $err:ty, $inner_fn:expr) => {
217 $crate::parse_int::impl_tryfrom_str!(&str, $to, $err, $inner_fn);
218 #[cfg(feature = "alloc")]
219 $crate::parse_int::impl_tryfrom_str!(alloc::string::String, $to, $err, $inner_fn; alloc::boxed::Box<str>, $to, $err, $inner_fn);
220
221 impl $crate::_export::_core::str::FromStr for $to {
222 type Err = $err;
223
224 fn from_str(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Err> {
225 $inner_fn(s)
226 }
227 }
228 }
229}
230pub(crate) use impl_parse_str;
231
232macro_rules! impl_tryfrom_str {
234 ($($from:ty, $to:ty, $err:ty, $inner_fn:expr);*) => {
235 $(
236 impl $crate::_export::_core::convert::TryFrom<$from> for $to {
237 type Error = $err;
238
239 fn try_from(s: $from) -> $crate::_export::_core::result::Result<Self, Self::Error> {
240 $inner_fn(s)
241 }
242 }
243 )*
244 }
245}
246pub(crate) use impl_tryfrom_str;
247
248pub fn hex_remove_prefix(s: &str) -> Result<&str, PrefixedHexError> {
254 if let Some(checked) = s.strip_prefix("0x") {
255 Ok(checked)
256 } else if let Some(checked) = s.strip_prefix("0X") {
257 Ok(checked)
258 } else {
259 Err(MissingPrefixError::new(s).into())
260 }
261}
262
263pub fn hex_check_unprefixed(s: &str) -> Result<&str, UnprefixedHexError> {
269 if s.starts_with("0x") || s.starts_with("0X") {
270 return Err(ContainsPrefixError::new(s).into());
271 }
272 Ok(s)
273}
274
275pub fn hex_u32(s: &str) -> Result<u32, ParseIntError> {
283 let unchecked = hex_remove_optional_prefix(s);
284 hex_u32_unchecked(unchecked)
285}
286
287pub fn hex_u32_prefixed(s: &str) -> Result<u32, PrefixedHexError> {
294 let checked = hex_remove_prefix(s)?;
295 Ok(hex_u32_unchecked(checked)?)
296}
297
298pub fn hex_u32_unprefixed(s: &str) -> Result<u32, UnprefixedHexError> {
305 let checked = hex_check_unprefixed(s)?;
306 Ok(hex_u32_unchecked(checked)?)
307}
308
309pub fn hex_u32_unchecked(s: &str) -> Result<u32, ParseIntError> {
316 u32::from_str_radix(s, 16).map_err(|error| ParseIntError {
317 input: s.into(),
318 bits: 32,
319 is_signed: false,
320 source: error,
321 })
322}
323
324pub fn hex_u128(s: &str) -> Result<u128, ParseIntError> {
332 let unchecked = hex_remove_optional_prefix(s);
333 hex_u128_unchecked(unchecked)
334}
335
336pub fn hex_u128_prefixed(s: &str) -> Result<u128, PrefixedHexError> {
343 let checked = hex_remove_prefix(s)?;
344 Ok(hex_u128_unchecked(checked)?)
345}
346
347pub fn hex_u128_unprefixed(s: &str) -> Result<u128, UnprefixedHexError> {
354 let checked = hex_check_unprefixed(s)?;
355 Ok(hex_u128_unchecked(checked)?)
356}
357
358pub fn hex_u128_unchecked(s: &str) -> Result<u128, ParseIntError> {
365 u128::from_str_radix(s, 16).map_err(|error| ParseIntError {
366 input: s.into(),
367 bits: 128,
368 is_signed: false,
369 source: error,
370 })
371}
372
373pub(crate) fn hex_remove_optional_prefix(s: &str) -> &str {
375 if let Some(stripped) = s.strip_prefix("0x") {
376 stripped
377 } else if let Some(stripped) = s.strip_prefix("0X") {
378 stripped
379 } else {
380 s
381 }
382}
383
384#[derive(Debug, Clone, Eq, PartialEq)]
386pub struct PrefixedHexError(PrefixedHexErrorInner);
387
388#[derive(Debug, Clone, Eq, PartialEq)]
390enum PrefixedHexErrorInner {
391 MissingPrefix(MissingPrefixError),
393 ParseInt(ParseIntError),
395}
396
397impl From<Infallible> for PrefixedHexError {
398 fn from(never: Infallible) -> Self { match never {} }
399}
400
401impl From<Infallible> for PrefixedHexErrorInner {
402 fn from(never: Infallible) -> Self { match never {} }
403}
404
405impl fmt::Display for PrefixedHexError {
406 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
407 use PrefixedHexErrorInner as E;
408
409 match self.0 {
410 E::MissingPrefix(ref e) => write_err!(f, "hex string is missing prefix"; e),
411 E::ParseInt(ref e) => write_err!(f, "prefixed hex string invalid int"; e),
412 }
413 }
414}
415
416#[cfg(feature = "std")]
417impl std::error::Error for PrefixedHexError {
418 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
419 use PrefixedHexErrorInner as E;
420
421 match self.0 {
422 E::MissingPrefix(ref e) => Some(e),
423 E::ParseInt(ref e) => Some(e),
424 }
425 }
426}
427
428impl From<MissingPrefixError> for PrefixedHexError {
429 fn from(e: MissingPrefixError) -> Self { Self(PrefixedHexErrorInner::MissingPrefix(e)) }
430}
431
432impl From<ParseIntError> for PrefixedHexError {
433 fn from(e: ParseIntError) -> Self { Self(PrefixedHexErrorInner::ParseInt(e)) }
434}
435
436#[derive(Debug, Clone, Eq, PartialEq)]
438pub struct UnprefixedHexError(UnprefixedHexErrorInner);
439
440#[derive(Debug, Clone, Eq, PartialEq)]
441enum UnprefixedHexErrorInner {
442 ContainsPrefix(ContainsPrefixError),
444 ParseInt(ParseIntError),
446}
447
448impl From<Infallible> for UnprefixedHexError {
449 fn from(never: Infallible) -> Self { match never {} }
450}
451
452impl From<Infallible> for UnprefixedHexErrorInner {
453 fn from(never: Infallible) -> Self { match never {} }
454}
455
456impl fmt::Display for UnprefixedHexError {
457 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
458 use UnprefixedHexErrorInner as E;
459
460 match self.0 {
461 E::ContainsPrefix(ref e) => write_err!(f, "hex string is contains prefix"; e),
462 E::ParseInt(ref e) => write_err!(f, "hex string parse int"; e),
463 }
464 }
465}
466
467#[cfg(feature = "std")]
468impl std::error::Error for UnprefixedHexError {
469 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
470 use UnprefixedHexErrorInner as E;
471
472 match self.0 {
473 E::ContainsPrefix(ref e) => Some(e),
474 E::ParseInt(ref e) => Some(e),
475 }
476 }
477}
478
479impl From<ContainsPrefixError> for UnprefixedHexError {
480 fn from(e: ContainsPrefixError) -> Self { Self(UnprefixedHexErrorInner::ContainsPrefix(e)) }
481}
482
483impl From<ParseIntError> for UnprefixedHexError {
484 fn from(e: ParseIntError) -> Self { Self(UnprefixedHexErrorInner::ParseInt(e)) }
485}
486
487#[derive(Debug, Clone, Eq, PartialEq)]
489struct MissingPrefixError {
490 hex: InputString,
491}
492
493impl MissingPrefixError {
494 pub(crate) fn new(hex: &str) -> Self { Self { hex: hex.into() } }
496}
497
498impl fmt::Display for MissingPrefixError {
499 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
500 write!(f, "{} because it is missing the '0x' prefix", self.hex.display_cannot_parse("hex"))
501 }
502}
503
504#[cfg(feature = "std")]
505impl std::error::Error for MissingPrefixError {}
506
507#[derive(Debug, Clone, Eq, PartialEq)]
509struct ContainsPrefixError {
510 hex: InputString,
511}
512
513impl ContainsPrefixError {
514 pub(crate) fn new(hex: &str) -> Self { Self { hex: hex.into() } }
516}
517
518impl fmt::Display for ContainsPrefixError {
519 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
520 write!(f, "{} because it contains the '0x' prefix", self.hex.display_cannot_parse("hex"))
521 }
522}
523
524#[cfg(feature = "std")]
525impl std::error::Error for ContainsPrefixError {}
526
527#[cfg(test)]
528mod tests {
529 #[cfg(feature = "std")]
530 use std::panic;
531
532 use super::*;
533
534 #[test]
535 fn parse_int() {
536 assert!(int_from_str::<u8>("1").is_ok());
537 let _ = int_from_str::<i8>("not a number").map_err(|e| assert!(e.is_signed));
538 let _ = int_from_str::<u8>("not a number").map_err(|e| assert!(!e.is_signed));
539 }
540
541 #[test]
542 #[cfg(feature = "std")]
543 fn parse_int_panic_when_populating_bits() {
544 #[allow(dead_code)]
546 struct TestTypeLargerThanU128(u128, u128);
547 impl_integer!(TestTypeLargerThanU128);
548 impl FromStr for TestTypeLargerThanU128 {
549 type Err = core::num::ParseIntError;
550
551 fn from_str(_: &str) -> Result<Self, Self::Err> {
552 "Always invalid for testing".parse::<u32>().map(|_| Self(0, 0))
553 }
554 }
555 impl From<i8> for TestTypeLargerThanU128 {
556 fn from(_: i8) -> Self { Self(0, 0) }
557 }
558
559 let result = panic::catch_unwind(|| int_from_str::<TestTypeLargerThanU128>("not a number"));
560 assert!(result.is_err());
561 }
562
563 #[test]
564 fn remove_prefix() {
565 let lower = "0xhello";
566 assert_eq!(hex_remove_prefix(lower).unwrap(), "hello");
567
568 let upper = "0Xhello";
569 assert_eq!(hex_remove_prefix(upper).unwrap(), "hello");
570
571 let err = "error";
572 assert!(hex_remove_prefix(err).is_err());
573 }
574
575 #[test]
576 fn check_unprefixed() {
577 let lower = "0xhello";
578 assert!(hex_check_unprefixed(lower).is_err());
579
580 let upper = "0Xhello";
581 assert!(hex_check_unprefixed(upper).is_err());
582
583 let valid = "hello";
584 assert_eq!(hex_check_unprefixed(valid).unwrap(), "hello");
585 }
586
587 #[test]
588 fn parse_u32_from_hex_prefixed() {
589 let want = 171;
590 let got = hex_u32("0xab").expect("failed to parse prefixed hex");
591 assert_eq!(got, want);
592 }
593
594 #[test]
595 fn parse_u32_from_hex_prefixed_upper() {
596 let want = 171;
597 let got = hex_u32("0XAB").expect("failed to parse prefixed hex");
598 assert_eq!(got, want);
599 }
600
601 #[test]
602 fn parse_u32_from_hex_no_prefix() {
603 let want = 171;
604 let got = hex_u32("ab").expect("failed to parse non-prefixed hex");
605 assert_eq!(got, want);
606 }
607
608 #[test]
609 fn parse_hex_u32_prefixed() {
610 let want = 171; assert_eq!(hex_u32_prefixed("0xab").unwrap(), want);
612 assert!(hex_u32_unprefixed("0xab").is_err());
613 }
614
615 #[test]
616 fn parse_hex_u32_upper_prefixed() {
617 let want = 171; assert_eq!(hex_u32_prefixed("0Xab").unwrap(), want);
619 assert!(hex_u32_unprefixed("0Xab").is_err());
620 }
621
622 #[test]
623 fn parse_hex_u32_unprefixed() {
624 let want = 171; assert_eq!(hex_u32_unprefixed("ab").unwrap(), want);
626 assert!(hex_u32_prefixed("ab").is_err());
627 }
628
629 #[test]
630 fn parse_u128_from_hex_prefixed() {
631 let want = 3_735_928_559;
632 let got = hex_u128("0xdeadbeef").expect("failed to parse prefixed hex");
633 assert_eq!(got, want);
634 }
635
636 #[test]
637 fn parse_u128_from_hex_upper_prefixed() {
638 let want = 3_735_928_559;
639 let got = hex_u128("0Xdeadbeef").expect("failed to parse prefixed hex");
640 assert_eq!(got, want);
641 }
642
643 #[test]
644 fn parse_u128_from_hex_no_prefix() {
645 let want = 3_735_928_559;
646 let got = hex_u128("deadbeef").expect("failed to parse non-prefixed hex");
647 assert_eq!(got, want);
648 }
649
650 #[test]
651 fn parse_hex_u128_prefixed() {
652 let want = 3_735_928_559;
653 assert_eq!(hex_u128_prefixed("0xdeadbeef").unwrap(), want);
654 assert!(hex_u128_unprefixed("0xdeadbeef").is_err());
655 }
656
657 #[test]
658 fn parse_hex_u128_upper_prefixed() {
659 let want = 3_735_928_559;
660 assert_eq!(hex_u128_prefixed("0Xdeadbeef").unwrap(), want);
661 assert!(hex_u128_unprefixed("0Xdeadbeef").is_err());
662 }
663
664 #[test]
665 fn parse_hex_u128_unprefixed() {
666 let want = 3_735_928_559;
667 assert_eq!(hex_u128_unprefixed("deadbeef").unwrap(), want);
668 assert!(hex_u128_prefixed("deadbeef").is_err());
669 }
670
671 #[test]
672 fn parse_u32_from_hex_unchecked_errors_on_prefix() {
673 assert!(hex_u32_unchecked("0xab").is_err());
674 assert!(hex_u32_unchecked("0Xab").is_err());
675 }
676
677 #[test]
678 fn parse_u32_from_hex_unchecked_errors_on_overflow() {
679 assert!(hex_u32_unchecked("1234abcd").is_ok());
680 assert!(hex_u32_unchecked("1234abcd1").is_err());
681 }
682
683 #[test]
684 fn parse_u128_from_hex_unchecked_errors_on_prefix() {
685 assert!(hex_u128_unchecked("0xdeadbeef").is_err());
686 assert!(hex_u128_unchecked("0Xdeadbeef").is_err());
687 }
688
689 #[test]
690 fn parse_u128_from_hex_unchecked_errors_on_overflow() {
691 assert!(hex_u128_unchecked("deadbeefabcdffffdeadbeefabcdffff").is_ok());
692 assert!(hex_u128_unchecked("deadbeefabcdffffdeadbeefabcdffff1").is_err());
693 }
694}