use core::convert::Infallible;
use core::fmt;
use core::str::FromStr;
use internals::error::InputString;
use internals::write_err;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct ParseIntError {
pub(crate) input: InputString,
pub(crate) bits: u8,
pub(crate) is_signed: bool,
pub(crate) source: core::num::ParseIntError,
}
impl fmt::Display for ParseIntError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let signed = if self.is_signed { "signed" } else { "unsigned" };
write_err!(f, "{} ({}, {}-bit)", self.input.display_cannot_parse("integer"), signed, self.bits; self.source)
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseIntError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.source) }
}
impl From<ParseIntError> for core::num::ParseIntError {
fn from(value: ParseIntError) -> Self { value.source }
}
impl AsRef<core::num::ParseIntError> for ParseIntError {
fn as_ref(&self) -> &core::num::ParseIntError { &self.source }
}
pub trait Integer:
FromStr<Err = core::num::ParseIntError> + TryFrom<i8> + Sized + sealed::Sealed
{
}
macro_rules! impl_integer {
($($type:ty),* $(,)?) => {
$(
impl Integer for $type {}
impl sealed::Sealed for $type {}
)*
}
}
impl_integer!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128);
mod sealed {
pub trait Sealed {}
}
pub fn int_from_str<T: Integer>(s: &str) -> Result<T, ParseIntError> { int(s) }
#[cfg(feature = "alloc")]
pub fn int_from_string<T: Integer>(s: alloc::string::String) -> Result<T, ParseIntError> { int(s) }
#[cfg(feature = "alloc")]
pub fn int_from_box<T: Integer>(s: alloc::boxed::Box<str>) -> Result<T, ParseIntError> { int(s) }
fn int<T: Integer, S: AsRef<str> + Into<InputString>>(s: S) -> Result<T, ParseIntError> {
s.as_ref().parse().map_err(|error| {
ParseIntError {
input: s.into(),
bits: u8::try_from(core::mem::size_of::<T>() * 8).expect("max is 128 bits for u128"),
is_signed: T::try_from(-1i8).is_ok(),
source: error,
}
})
}
macro_rules! impl_parse_str_from_int_infallible {
($to:ident, $inner:ident, $fn:ident) => {
impl $crate::_export::_core::str::FromStr for $to {
type Err = $crate::parse_int::ParseIntError;
fn from_str(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Err> {
$crate::_export::_core::convert::TryFrom::try_from(s)
}
}
impl $crate::_export::_core::convert::TryFrom<&str> for $to {
type Error = $crate::parse_int::ParseIntError;
fn try_from(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Error> {
$crate::parse_int::int_from_str::<$inner>(s).map($to::$fn)
}
}
#[cfg(feature = "alloc")]
impl $crate::_export::_core::convert::TryFrom<alloc::string::String> for $to {
type Error = $crate::parse_int::ParseIntError;
fn try_from(
s: alloc::string::String,
) -> $crate::_export::_core::result::Result<Self, Self::Error> {
$crate::parse_int::int_from_string::<$inner>(s).map($to::$fn)
}
}
#[cfg(feature = "alloc")]
impl $crate::_export::_core::convert::TryFrom<alloc::boxed::Box<str>> for $to {
type Error = $crate::parse_int::ParseIntError;
fn try_from(
s: alloc::boxed::Box<str>,
) -> $crate::_export::_core::result::Result<Self, Self::Error> {
$crate::parse_int::int_from_box::<$inner>(s).map($to::$fn)
}
}
};
}
pub(crate) use impl_parse_str_from_int_infallible;
macro_rules! impl_parse_str {
($to:ty, $err:ty, $inner_fn:expr) => {
$crate::parse_int::impl_tryfrom_str!(&str, $to, $err, $inner_fn);
#[cfg(feature = "alloc")]
$crate::parse_int::impl_tryfrom_str!(alloc::string::String, $to, $err, $inner_fn; alloc::boxed::Box<str>, $to, $err, $inner_fn);
impl $crate::_export::_core::str::FromStr for $to {
type Err = $err;
fn from_str(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Err> {
$inner_fn(s)
}
}
}
}
pub(crate) use impl_parse_str;
macro_rules! impl_tryfrom_str {
($($from:ty, $to:ty, $err:ty, $inner_fn:expr);*) => {
$(
impl $crate::_export::_core::convert::TryFrom<$from> for $to {
type Error = $err;
fn try_from(s: $from) -> $crate::_export::_core::result::Result<Self, Self::Error> {
$inner_fn(s)
}
}
)*
}
}
pub(crate) use impl_tryfrom_str;
pub fn hex_remove_prefix(s: &str) -> Result<&str, PrefixedHexError> {
if let Some(checked) = s.strip_prefix("0x") {
Ok(checked)
} else if let Some(checked) = s.strip_prefix("0X") {
Ok(checked)
} else {
Err(MissingPrefixError::new(s).into())
}
}
pub fn hex_check_unprefixed(s: &str) -> Result<&str, UnprefixedHexError> {
if s.starts_with("0x") || s.starts_with("0X") {
return Err(ContainsPrefixError::new(s).into());
}
Ok(s)
}
pub fn hex_u32(s: &str) -> Result<u32, ParseIntError> {
let unchecked = hex_remove_optional_prefix(s);
hex_u32_unchecked(unchecked)
}
pub fn hex_u32_prefixed(s: &str) -> Result<u32, PrefixedHexError> {
let checked = hex_remove_prefix(s)?;
Ok(hex_u32_unchecked(checked)?)
}
pub fn hex_u32_unprefixed(s: &str) -> Result<u32, UnprefixedHexError> {
let checked = hex_check_unprefixed(s)?;
Ok(hex_u32_unchecked(checked)?)
}
pub fn hex_u32_unchecked(s: &str) -> Result<u32, ParseIntError> {
u32::from_str_radix(s, 16).map_err(|error| ParseIntError {
input: s.into(),
bits: 32,
is_signed: false,
source: error,
})
}
pub fn hex_u128(s: &str) -> Result<u128, ParseIntError> {
let unchecked = hex_remove_optional_prefix(s);
hex_u128_unchecked(unchecked)
}
pub fn hex_u128_prefixed(s: &str) -> Result<u128, PrefixedHexError> {
let checked = hex_remove_prefix(s)?;
Ok(hex_u128_unchecked(checked)?)
}
pub fn hex_u128_unprefixed(s: &str) -> Result<u128, UnprefixedHexError> {
let checked = hex_check_unprefixed(s)?;
Ok(hex_u128_unchecked(checked)?)
}
pub fn hex_u128_unchecked(s: &str) -> Result<u128, ParseIntError> {
u128::from_str_radix(s, 16).map_err(|error| ParseIntError {
input: s.into(),
bits: 128,
is_signed: false,
source: error,
})
}
pub(crate) fn hex_remove_optional_prefix(s: &str) -> &str {
if let Some(stripped) = s.strip_prefix("0x") {
stripped
} else if let Some(stripped) = s.strip_prefix("0X") {
stripped
} else {
s
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PrefixedHexError(PrefixedHexErrorInner);
#[derive(Debug, Clone, Eq, PartialEq)]
enum PrefixedHexErrorInner {
MissingPrefix(MissingPrefixError),
ParseInt(ParseIntError),
}
impl From<Infallible> for PrefixedHexError {
fn from(never: Infallible) -> Self { match never {} }
}
impl From<Infallible> for PrefixedHexErrorInner {
fn from(never: Infallible) -> Self { match never {} }
}
impl fmt::Display for PrefixedHexError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use PrefixedHexErrorInner as E;
match self.0 {
E::MissingPrefix(ref e) => write_err!(f, "hex string is missing prefix"; e),
E::ParseInt(ref e) => write_err!(f, "prefixed hex string invalid int"; e),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for PrefixedHexError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use PrefixedHexErrorInner as E;
match self.0 {
E::MissingPrefix(ref e) => Some(e),
E::ParseInt(ref e) => Some(e),
}
}
}
impl From<MissingPrefixError> for PrefixedHexError {
fn from(e: MissingPrefixError) -> Self { Self(PrefixedHexErrorInner::MissingPrefix(e)) }
}
impl From<ParseIntError> for PrefixedHexError {
fn from(e: ParseIntError) -> Self { Self(PrefixedHexErrorInner::ParseInt(e)) }
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct UnprefixedHexError(UnprefixedHexErrorInner);
#[derive(Debug, Clone, Eq, PartialEq)]
enum UnprefixedHexErrorInner {
ContainsPrefix(ContainsPrefixError),
ParseInt(ParseIntError),
}
impl From<Infallible> for UnprefixedHexError {
fn from(never: Infallible) -> Self { match never {} }
}
impl From<Infallible> for UnprefixedHexErrorInner {
fn from(never: Infallible) -> Self { match never {} }
}
impl fmt::Display for UnprefixedHexError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use UnprefixedHexErrorInner as E;
match self.0 {
E::ContainsPrefix(ref e) => write_err!(f, "hex string is contains prefix"; e),
E::ParseInt(ref e) => write_err!(f, "hex string parse int"; e),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for UnprefixedHexError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use UnprefixedHexErrorInner as E;
match self.0 {
E::ContainsPrefix(ref e) => Some(e),
E::ParseInt(ref e) => Some(e),
}
}
}
impl From<ContainsPrefixError> for UnprefixedHexError {
fn from(e: ContainsPrefixError) -> Self { Self(UnprefixedHexErrorInner::ContainsPrefix(e)) }
}
impl From<ParseIntError> for UnprefixedHexError {
fn from(e: ParseIntError) -> Self { Self(UnprefixedHexErrorInner::ParseInt(e)) }
}
#[derive(Debug, Clone, Eq, PartialEq)]
struct MissingPrefixError {
hex: InputString,
}
impl MissingPrefixError {
pub(crate) fn new(hex: &str) -> Self { Self { hex: hex.into() } }
}
impl fmt::Display for MissingPrefixError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} because it is missing the '0x' prefix", self.hex.display_cannot_parse("hex"))
}
}
#[cfg(feature = "std")]
impl std::error::Error for MissingPrefixError {}
#[derive(Debug, Clone, Eq, PartialEq)]
struct ContainsPrefixError {
hex: InputString,
}
impl ContainsPrefixError {
pub(crate) fn new(hex: &str) -> Self { Self { hex: hex.into() } }
}
impl fmt::Display for ContainsPrefixError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} because it contains the '0x' prefix", self.hex.display_cannot_parse("hex"))
}
}
#[cfg(feature = "std")]
impl std::error::Error for ContainsPrefixError {}
#[cfg(test)]
mod tests {
#[cfg(feature = "std")]
use std::panic;
use super::*;
#[test]
fn parse_int() {
assert!(int_from_str::<u8>("1").is_ok());
let _ = int_from_str::<i8>("not a number").map_err(|e| assert!(e.is_signed));
let _ = int_from_str::<u8>("not a number").map_err(|e| assert!(!e.is_signed));
}
#[test]
#[cfg(feature = "std")]
fn parse_int_panic_when_populating_bits() {
#[allow(dead_code)]
struct TestTypeLargerThanU128(u128, u128);
impl_integer!(TestTypeLargerThanU128);
impl FromStr for TestTypeLargerThanU128 {
type Err = core::num::ParseIntError;
fn from_str(_: &str) -> Result<Self, Self::Err> {
"Always invalid for testing".parse::<u32>().map(|_| Self(0, 0))
}
}
impl From<i8> for TestTypeLargerThanU128 {
fn from(_: i8) -> Self { Self(0, 0) }
}
let result = panic::catch_unwind(|| int_from_str::<TestTypeLargerThanU128>("not a number"));
assert!(result.is_err());
}
#[test]
fn remove_prefix() {
let lower = "0xhello";
assert_eq!(hex_remove_prefix(lower).unwrap(), "hello");
let upper = "0Xhello";
assert_eq!(hex_remove_prefix(upper).unwrap(), "hello");
let err = "error";
assert!(hex_remove_prefix(err).is_err());
}
#[test]
fn check_unprefixed() {
let lower = "0xhello";
assert!(hex_check_unprefixed(lower).is_err());
let upper = "0Xhello";
assert!(hex_check_unprefixed(upper).is_err());
let valid = "hello";
assert_eq!(hex_check_unprefixed(valid).unwrap(), "hello");
}
#[test]
fn parse_u32_from_hex_prefixed() {
let want = 171;
let got = hex_u32("0xab").expect("failed to parse prefixed hex");
assert_eq!(got, want);
}
#[test]
fn parse_u32_from_hex_prefixed_upper() {
let want = 171;
let got = hex_u32("0XAB").expect("failed to parse prefixed hex");
assert_eq!(got, want);
}
#[test]
fn parse_u32_from_hex_no_prefix() {
let want = 171;
let got = hex_u32("ab").expect("failed to parse non-prefixed hex");
assert_eq!(got, want);
}
#[test]
fn parse_hex_u32_prefixed() {
let want = 171; assert_eq!(hex_u32_prefixed("0xab").unwrap(), want);
assert!(hex_u32_unprefixed("0xab").is_err());
}
#[test]
fn parse_hex_u32_upper_prefixed() {
let want = 171; assert_eq!(hex_u32_prefixed("0Xab").unwrap(), want);
assert!(hex_u32_unprefixed("0Xab").is_err());
}
#[test]
fn parse_hex_u32_unprefixed() {
let want = 171; assert_eq!(hex_u32_unprefixed("ab").unwrap(), want);
assert!(hex_u32_prefixed("ab").is_err());
}
#[test]
fn parse_u128_from_hex_prefixed() {
let want = 3_735_928_559;
let got = hex_u128("0xdeadbeef").expect("failed to parse prefixed hex");
assert_eq!(got, want);
}
#[test]
fn parse_u128_from_hex_upper_prefixed() {
let want = 3_735_928_559;
let got = hex_u128("0Xdeadbeef").expect("failed to parse prefixed hex");
assert_eq!(got, want);
}
#[test]
fn parse_u128_from_hex_no_prefix() {
let want = 3_735_928_559;
let got = hex_u128("deadbeef").expect("failed to parse non-prefixed hex");
assert_eq!(got, want);
}
#[test]
fn parse_hex_u128_prefixed() {
let want = 3_735_928_559;
assert_eq!(hex_u128_prefixed("0xdeadbeef").unwrap(), want);
assert!(hex_u128_unprefixed("0xdeadbeef").is_err());
}
#[test]
fn parse_hex_u128_upper_prefixed() {
let want = 3_735_928_559;
assert_eq!(hex_u128_prefixed("0Xdeadbeef").unwrap(), want);
assert!(hex_u128_unprefixed("0Xdeadbeef").is_err());
}
#[test]
fn parse_hex_u128_unprefixed() {
let want = 3_735_928_559;
assert_eq!(hex_u128_unprefixed("deadbeef").unwrap(), want);
assert!(hex_u128_prefixed("deadbeef").is_err());
}
#[test]
fn parse_u32_from_hex_unchecked_errors_on_prefix() {
assert!(hex_u32_unchecked("0xab").is_err());
assert!(hex_u32_unchecked("0Xab").is_err());
}
#[test]
fn parse_u32_from_hex_unchecked_errors_on_overflow() {
assert!(hex_u32_unchecked("1234abcd").is_ok());
assert!(hex_u32_unchecked("1234abcd1").is_err());
}
#[test]
fn parse_u128_from_hex_unchecked_errors_on_prefix() {
assert!(hex_u128_unchecked("0xdeadbeef").is_err());
assert!(hex_u128_unchecked("0Xdeadbeef").is_err());
}
#[test]
fn parse_u128_from_hex_unchecked_errors_on_overflow() {
assert!(hex_u128_unchecked("deadbeefabcdffffdeadbeefabcdffff").is_ok());
assert!(hex_u128_unchecked("deadbeefabcdffffdeadbeefabcdffff1").is_err());
}
}