diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 5b7d5f45d92..1479b891fa2 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -112,6 +112,112 @@ nonzero_integers! { #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroIsize(isize); } +/// An error which can be returned when parsing a non-zero integer. +/// +/// # Potential causes +/// +/// Among other causes, `ParseNonZeroIntError` can be thrown because of leading or trailing +/// whitespace in the string e.g., when it is obtained from the standard input. +/// Using the [`str.trim()`] method ensures that no whitespace remains before parsing. +/// +/// [`str.trim()`]: ../../std/primitive.str.html#method.trim +#[unstable(feature = "nonzero_parse", issue = "0")] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ParseNonZeroIntError { + kind: NonZeroIntErrorKind, +} + +/// Enum to store the various types of errors that can cause parsing a non-zero integer to fail. +#[unstable(feature = "nonzero_parse", issue = "0")] +#[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum NonZeroIntErrorKind { + /// Value being parsed is empty. + /// + /// Among other causes, this variant will be constructed when parsing an empty string. + Empty, + /// Contains an invalid digit. + /// + /// Among other causes, this variant will be constructed when parsing a string that + /// contains a letter. + InvalidDigit, + /// Integer is too large to store in target integer type. + Overflow, + /// Integer is too small to store in target integer type. + Underflow, + /// Integer contains the value `0` which is forbidden for a non-zero integer + Zero, +} + +#[unstable(feature = "nonzero_parse", issue = "0")] +impl From for ParseNonZeroIntError { + fn from(p: ParseIntError) -> Self { + use self::IntErrorKind as IK; + use self::NonZeroIntErrorKind as NK; + ParseNonZeroIntError { + kind: match p.kind { + IK::Empty => NK::Empty, + IK::InvalidDigit => NK::InvalidDigit, + IK::Overflow => NK::Overflow, + IK::Underflow => NK::Underflow, + }, + } + } +} + +impl ParseNonZeroIntError { + /// Outputs the detailed cause of parsing an integer failing. + #[unstable(feature = "int_error_matching", + reason = "it can be useful to match errors when making error messages \ + for integer parsing", + issue = "22639")] + pub fn kind(&self) -> &NonZeroIntErrorKind { + &self.kind + } + + #[unstable(feature = "int_error_internals", + reason = "available through Error trait and this method should \ + not be exposed publicly", + issue = "0")] + #[doc(hidden)] + pub fn __description(&self) -> &str { + match self.kind { + NonZeroIntErrorKind::Empty => "cannot parse integer from empty string", + NonZeroIntErrorKind::InvalidDigit => "invalid digit found in string", + NonZeroIntErrorKind::Overflow => "number too large to fit in target type", + NonZeroIntErrorKind::Underflow => "number too small to fit in target type", + NonZeroIntErrorKind::Zero => "number is 0", + } + } + +} + +#[unstable(feature = "nonzero_parse", issue = "0")] +impl fmt::Display for ParseNonZeroIntError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.__description().fmt(f) + } +} + + +macro_rules! from_str_radix_nzint_impl { + ($($t:ty)*) => {$( + #[unstable(feature = "nonzero_parse", issue = "0")] + impl FromStr for $t { + type Err = ParseNonZeroIntError; + fn from_str(src: &str) -> Result { + Self::new(from_str_radix(src, 10)?) + .ok_or(ParseNonZeroIntError { + kind: NonZeroIntErrorKind::Zero + }) + } + } + )*} +} + +from_str_radix_nzint_impl! { NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize + NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 NonZeroIsize } + /// Provides intentionally-wrapped arithmetic on `T`. /// /// Operations like `+` on `u32` values is intended to never overflow, diff --git a/src/libcore/tests/nonzero.rs b/src/libcore/tests/nonzero.rs index 4532568ee0c..6e2d4588edc 100644 --- a/src/libcore/tests/nonzero.rs +++ b/src/libcore/tests/nonzero.rs @@ -126,3 +126,20 @@ fn test_from_signed_nonzero() { let num: i32 = nz.into(); assert_eq!(num, 1i32); } + +#[test] +fn test_from_str() { + assert_eq!(FromStr::from_str("123"), Ok(NonZeroU8::new(123).unwrap())); + assert_eq!( + FromStr::from_str("0"), + Err(ParseNonZeroIntError { + kind: NonZeroIntErrorKind::Zero + }) + ); + assert_eq!( + FromStr::from_str("-1", + Err(ParseNonZeroIntError { + kind: NonZeroIntErrorKind::Underflow + }) + ); +}