Close amounts error types
Close the two pubic enum error types in `units::amounts`. All the other structs are closed already because they either have private fields or marked `non_exhaustive`.
This commit is contained in:
parent
23c77275b1
commit
fd2a5c1ec7
|
@ -11,8 +11,10 @@ use super::INPUT_STRING_LEN_LIMIT;
|
||||||
|
|
||||||
/// An error during amount parsing amount with denomination.
|
/// An error during amount parsing amount with denomination.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
#[non_exhaustive]
|
pub struct ParseError(pub(crate) ParseErrorInner);
|
||||||
pub enum ParseError {
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub(crate) enum ParseErrorInner {
|
||||||
/// Invalid amount.
|
/// Invalid amount.
|
||||||
Amount(ParseAmountError),
|
Amount(ParseAmountError),
|
||||||
/// Invalid denomination.
|
/// Invalid denomination.
|
||||||
|
@ -22,42 +24,43 @@ pub enum ParseError {
|
||||||
}
|
}
|
||||||
|
|
||||||
internals::impl_from_infallible!(ParseError);
|
internals::impl_from_infallible!(ParseError);
|
||||||
|
internals::impl_from_infallible!(ParseErrorInner);
|
||||||
|
|
||||||
impl From<ParseAmountError> for ParseError {
|
impl From<ParseAmountError> for ParseError {
|
||||||
fn from(e: ParseAmountError) -> Self { Self::Amount(e) }
|
fn from(e: ParseAmountError) -> Self { Self(ParseErrorInner::Amount(e)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseDenominationError> for ParseError {
|
impl From<ParseDenominationError> for ParseError {
|
||||||
fn from(e: ParseDenominationError) -> Self { Self::Denomination(e) }
|
fn from(e: ParseDenominationError) -> Self { Self(ParseErrorInner::Denomination(e)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OutOfRangeError> for ParseError {
|
impl From<OutOfRangeError> for ParseError {
|
||||||
fn from(e: OutOfRangeError) -> Self { Self::Amount(e.into()) }
|
fn from(e: OutOfRangeError) -> Self { Self(ParseErrorInner::Amount(e.into())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TooPreciseError> for ParseError {
|
impl From<TooPreciseError> for ParseError {
|
||||||
fn from(e: TooPreciseError) -> Self { Self::Amount(e.into()) }
|
fn from(e: TooPreciseError) -> Self { Self(ParseErrorInner::Amount(e.into())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<MissingDigitsError> for ParseError {
|
impl From<MissingDigitsError> for ParseError {
|
||||||
fn from(e: MissingDigitsError) -> Self { Self::Amount(e.into()) }
|
fn from(e: MissingDigitsError) -> Self { Self(ParseErrorInner::Amount(e.into())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<InputTooLargeError> for ParseError {
|
impl From<InputTooLargeError> for ParseError {
|
||||||
fn from(e: InputTooLargeError) -> Self { Self::Amount(e.into()) }
|
fn from(e: InputTooLargeError) -> Self { Self(ParseErrorInner::Amount(e.into())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<InvalidCharacterError> for ParseError {
|
impl From<InvalidCharacterError> for ParseError {
|
||||||
fn from(e: InvalidCharacterError) -> Self { Self::Amount(e.into()) }
|
fn from(e: InvalidCharacterError) -> Self { Self(ParseErrorInner::Amount(e.into())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ParseError {
|
impl fmt::Display for ParseError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self.0 {
|
||||||
ParseError::Amount(error) => write_err!(f, "invalid amount"; error),
|
ParseErrorInner::Amount(ref e) => write_err!(f, "invalid amount"; e),
|
||||||
ParseError::Denomination(error) => write_err!(f, "invalid denomination"; error),
|
ParseErrorInner::Denomination(ref e) => write_err!(f, "invalid denomination"; e),
|
||||||
// We consider this to not be a source because it currently doesn't contain useful info.
|
// We consider this to not be a source because it currently doesn't contain useful info.
|
||||||
ParseError::MissingDenomination(_) =>
|
ParseErrorInner::MissingDenomination(_) =>
|
||||||
f.write_str("the input doesn't contain a denomination"),
|
f.write_str("the input doesn't contain a denomination"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,19 +69,21 @@ impl fmt::Display for ParseError {
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl std::error::Error for ParseError {
|
impl std::error::Error for ParseError {
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
match self {
|
match self.0 {
|
||||||
ParseError::Amount(error) => Some(error),
|
ParseErrorInner::Amount(ref e) => Some(e),
|
||||||
ParseError::Denomination(error) => Some(error),
|
ParseErrorInner::Denomination(ref e) => Some(e),
|
||||||
// We consider this to not be a source because it currently doesn't contain useful info.
|
// We consider this to not be a source because it currently doesn't contain useful info.
|
||||||
ParseError::MissingDenomination(_) => None,
|
ParseErrorInner::MissingDenomination(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error during amount parsing.
|
/// An error during amount parsing.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
#[non_exhaustive]
|
pub struct ParseAmountError(pub(crate) ParseAmountErrorInner);
|
||||||
pub enum ParseAmountError {
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub(crate) enum ParseAmountErrorInner {
|
||||||
/// The amount is too big or too small.
|
/// The amount is too big or too small.
|
||||||
OutOfRange(OutOfRangeError),
|
OutOfRange(OutOfRangeError),
|
||||||
/// Amount has higher precision than supported by the type.
|
/// Amount has higher precision than supported by the type.
|
||||||
|
@ -92,28 +97,31 @@ pub enum ParseAmountError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TooPreciseError> for ParseAmountError {
|
impl From<TooPreciseError> for ParseAmountError {
|
||||||
fn from(value: TooPreciseError) -> Self { Self::TooPrecise(value) }
|
fn from(value: TooPreciseError) -> Self { Self(ParseAmountErrorInner::TooPrecise(value)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<MissingDigitsError> for ParseAmountError {
|
impl From<MissingDigitsError> for ParseAmountError {
|
||||||
fn from(value: MissingDigitsError) -> Self { Self::MissingDigits(value) }
|
fn from(value: MissingDigitsError) -> Self { Self(ParseAmountErrorInner::MissingDigits(value)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<InputTooLargeError> for ParseAmountError {
|
impl From<InputTooLargeError> for ParseAmountError {
|
||||||
fn from(value: InputTooLargeError) -> Self { Self::InputTooLarge(value) }
|
fn from(value: InputTooLargeError) -> Self { Self(ParseAmountErrorInner::InputTooLarge(value)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<InvalidCharacterError> for ParseAmountError {
|
impl From<InvalidCharacterError> for ParseAmountError {
|
||||||
fn from(value: InvalidCharacterError) -> Self { Self::InvalidCharacter(value) }
|
fn from(value: InvalidCharacterError) -> Self {
|
||||||
|
Self(ParseAmountErrorInner::InvalidCharacter(value))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internals::impl_from_infallible!(ParseAmountError);
|
internals::impl_from_infallible!(ParseAmountError);
|
||||||
|
internals::impl_from_infallible!(ParseAmountErrorInner);
|
||||||
|
|
||||||
impl fmt::Display for ParseAmountError {
|
impl fmt::Display for ParseAmountError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use ParseAmountError::*;
|
use ParseAmountErrorInner::*;
|
||||||
|
|
||||||
match *self {
|
match self.0 {
|
||||||
OutOfRange(ref error) => write_err!(f, "amount out of range"; error),
|
OutOfRange(ref error) => write_err!(f, "amount out of range"; error),
|
||||||
TooPrecise(ref error) => write_err!(f, "amount has a too high precision"; error),
|
TooPrecise(ref error) => write_err!(f, "amount has a too high precision"; error),
|
||||||
MissingDigits(ref error) => write_err!(f, "the input has too few digits"; error),
|
MissingDigits(ref error) => write_err!(f, "the input has too few digits"; error),
|
||||||
|
@ -126,9 +134,9 @@ impl fmt::Display for ParseAmountError {
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl std::error::Error for ParseAmountError {
|
impl std::error::Error for ParseAmountError {
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
use ParseAmountError::*;
|
use ParseAmountErrorInner::*;
|
||||||
|
|
||||||
match *self {
|
match self.0 {
|
||||||
TooPrecise(ref error) => Some(error),
|
TooPrecise(ref error) => Some(error),
|
||||||
InputTooLarge(ref error) => Some(error),
|
InputTooLarge(ref error) => Some(error),
|
||||||
OutOfRange(ref error) => Some(error),
|
OutOfRange(ref error) => Some(error),
|
||||||
|
@ -195,7 +203,9 @@ impl fmt::Display for OutOfRangeError {
|
||||||
impl std::error::Error for OutOfRangeError {}
|
impl std::error::Error for OutOfRangeError {}
|
||||||
|
|
||||||
impl From<OutOfRangeError> for ParseAmountError {
|
impl From<OutOfRangeError> for ParseAmountError {
|
||||||
fn from(value: OutOfRangeError) -> Self { ParseAmountError::OutOfRange(value) }
|
fn from(value: OutOfRangeError) -> Self {
|
||||||
|
ParseAmountError(ParseAmountErrorInner::OutOfRange(value))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error returned when the input string has higher precision than satoshis.
|
/// Error returned when the input string has higher precision than satoshis.
|
||||||
|
|
|
@ -20,7 +20,7 @@ use core::cmp::Ordering;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
|
|
||||||
use self::error::MissingDigitsKind;
|
use self::error::{MissingDigitsKind, ParseAmountErrorInner, ParseErrorInner};
|
||||||
|
|
||||||
#[rustfmt::skip] // Keep public re-exports separate.
|
#[rustfmt::skip] // Keep public re-exports separate.
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
|
@ -297,10 +297,12 @@ impl InnerParseError {
|
||||||
match self {
|
match self {
|
||||||
Self::Overflow { is_negative } =>
|
Self::Overflow { is_negative } =>
|
||||||
OutOfRangeError { is_signed, is_greater_than_max: !is_negative }.into(),
|
OutOfRangeError { is_signed, is_greater_than_max: !is_negative }.into(),
|
||||||
Self::TooPrecise(error) => ParseAmountError::TooPrecise(error),
|
Self::TooPrecise(e) => ParseAmountError(ParseAmountErrorInner::TooPrecise(e)),
|
||||||
Self::MissingDigits(error) => ParseAmountError::MissingDigits(error),
|
Self::MissingDigits(e) => ParseAmountError(ParseAmountErrorInner::MissingDigits(e)),
|
||||||
Self::InputTooLarge(len) => ParseAmountError::InputTooLarge(InputTooLargeError { len }),
|
Self::InputTooLarge(len) =>
|
||||||
Self::InvalidCharacter(error) => ParseAmountError::InvalidCharacter(error),
|
ParseAmountError(ParseAmountErrorInner::InputTooLarge(InputTooLargeError { len })),
|
||||||
|
Self::InvalidCharacter(e) =>
|
||||||
|
ParseAmountError(ParseAmountErrorInner::InvalidCharacter(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,7 +313,7 @@ fn split_amount_and_denomination(s: &str) -> Result<(&str, Denomination), ParseE
|
||||||
} else {
|
} else {
|
||||||
let i = s
|
let i = s
|
||||||
.find(|c: char| c.is_alphabetic())
|
.find(|c: char| c.is_alphabetic())
|
||||||
.ok_or(ParseError::MissingDenomination(MissingDenominationError))?;
|
.ok_or(ParseError(ParseErrorInner::MissingDenomination(MissingDenominationError)))?;
|
||||||
(i, i)
|
(i, i)
|
||||||
};
|
};
|
||||||
Ok((&s[..i], s[j..].parse()?))
|
Ok((&s[..i], s[j..].parse()?))
|
||||||
|
|
|
@ -10,6 +10,7 @@ use core::{default, fmt, ops};
|
||||||
#[cfg(feature = "arbitrary")]
|
#[cfg(feature = "arbitrary")]
|
||||||
use arbitrary::{Arbitrary, Unstructured};
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
|
|
||||||
|
use super::error::{ParseAmountErrorInner, ParseErrorInner};
|
||||||
use super::{
|
use super::{
|
||||||
parse_signed_to_satoshi, split_amount_and_denomination, Amount, Denomination, Display,
|
parse_signed_to_satoshi, split_amount_and_denomination, Amount, Denomination, Display,
|
||||||
DisplayStyle, OutOfRangeError, ParseAmountError, ParseError,
|
DisplayStyle, OutOfRangeError, ParseAmountError, ParseError,
|
||||||
|
@ -92,12 +93,14 @@ impl SignedAmount {
|
||||||
pub fn from_str_in(s: &str, denom: Denomination) -> Result<SignedAmount, ParseAmountError> {
|
pub fn from_str_in(s: &str, denom: Denomination) -> Result<SignedAmount, ParseAmountError> {
|
||||||
match parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(true))? {
|
match parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(true))? {
|
||||||
// (negative, amount)
|
// (negative, amount)
|
||||||
(false, sat) if sat > i64::MAX as u64 =>
|
(false, sat) if sat > i64::MAX as u64 => Err(ParseAmountError(
|
||||||
Err(ParseAmountError::OutOfRange(OutOfRangeError::too_big(true))),
|
ParseAmountErrorInner::OutOfRange(OutOfRangeError::too_big(true)),
|
||||||
|
)),
|
||||||
(false, sat) => Ok(SignedAmount(sat as i64)),
|
(false, sat) => Ok(SignedAmount(sat as i64)),
|
||||||
(true, sat) if sat == i64::MIN.unsigned_abs() => Ok(SignedAmount(i64::MIN)),
|
(true, sat) if sat == i64::MIN.unsigned_abs() => Ok(SignedAmount(i64::MIN)),
|
||||||
(true, sat) if sat > i64::MIN.unsigned_abs() =>
|
(true, sat) if sat > i64::MIN.unsigned_abs() => Err(ParseAmountError(
|
||||||
Err(ParseAmountError::OutOfRange(OutOfRangeError::too_small())),
|
ParseAmountErrorInner::OutOfRange(OutOfRangeError::too_small()),
|
||||||
|
)),
|
||||||
(true, sat) => Ok(SignedAmount(-(sat as i64))),
|
(true, sat) => Ok(SignedAmount(-(sat as i64))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -415,7 +418,7 @@ impl FromStr for SignedAmount {
|
||||||
let result = SignedAmount::from_str_with_denomination(s);
|
let result = SignedAmount::from_str_with_denomination(s);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Err(ParseError::MissingDenomination(_)) => {
|
Err(ParseError(ParseErrorInner::MissingDenomination(_))) => {
|
||||||
let d = SignedAmount::from_str_in(s, Denomination::Satoshi);
|
let d = SignedAmount::from_str_in(s, Denomination::Satoshi);
|
||||||
|
|
||||||
if d == Ok(SignedAmount::ZERO) {
|
if d == Ok(SignedAmount::ZERO) {
|
||||||
|
|
|
@ -33,7 +33,9 @@ fn from_str_zero() {
|
||||||
match s.parse::<Amount>() {
|
match s.parse::<Amount>() {
|
||||||
Err(e) => assert_eq!(
|
Err(e) => assert_eq!(
|
||||||
e,
|
e,
|
||||||
ParseError::Amount(ParseAmountError::OutOfRange(OutOfRangeError::negative()))
|
ParseError(ParseErrorInner::Amount(ParseAmountError(
|
||||||
|
ParseAmountErrorInner::OutOfRange(OutOfRangeError::negative())
|
||||||
|
)))
|
||||||
),
|
),
|
||||||
Ok(_) => panic!("unsigned amount from {}", s),
|
Ok(_) => panic!("unsigned amount from {}", s),
|
||||||
}
|
}
|
||||||
|
@ -321,7 +323,7 @@ fn parsing() {
|
||||||
// more than 50 chars.
|
// more than 50 chars.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p("100000000000000.00000000000000000000000000000000000", Denomination::Bitcoin),
|
p("100000000000000.00000000000000000000000000000000000", Denomination::Bitcoin),
|
||||||
Err(E::InputTooLarge(InputTooLargeError { len: 51 }))
|
Err(E(ParseAmountErrorInner::InputTooLarge(InputTooLargeError { len: 51 })))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -731,10 +733,10 @@ fn serde_as_btc() {
|
||||||
// errors
|
// errors
|
||||||
let t: Result<T, serde_json::Error> =
|
let t: Result<T, serde_json::Error> =
|
||||||
serde_json::from_str("{\"amt\": 1000000.000000001, \"samt\": 1}");
|
serde_json::from_str("{\"amt\": 1000000.000000001, \"samt\": 1}");
|
||||||
assert!(t
|
assert!(t.unwrap_err().to_string().contains(
|
||||||
.unwrap_err()
|
&ParseAmountError(ParseAmountErrorInner::TooPrecise(TooPreciseError { position: 16 }))
|
||||||
.to_string()
|
.to_string()
|
||||||
.contains(&ParseAmountError::TooPrecise(TooPreciseError { position: 16 }).to_string()));
|
));
|
||||||
let t: Result<T, serde_json::Error> = serde_json::from_str("{\"amt\": -1, \"samt\": 1}");
|
let t: Result<T, serde_json::Error> = serde_json::from_str("{\"amt\": -1, \"samt\": 1}");
|
||||||
assert!(t.unwrap_err().to_string().contains(&OutOfRangeError::negative().to_string()));
|
assert!(t.unwrap_err().to_string().contains(&OutOfRangeError::negative().to_string()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ use ::serde::{Deserialize, Serialize};
|
||||||
#[cfg(feature = "arbitrary")]
|
#[cfg(feature = "arbitrary")]
|
||||||
use arbitrary::{Arbitrary, Unstructured};
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
|
|
||||||
|
use super::error::{ParseAmountErrorInner, ParseErrorInner};
|
||||||
use super::{
|
use super::{
|
||||||
parse_signed_to_satoshi, split_amount_and_denomination, Denomination, Display, DisplayStyle,
|
parse_signed_to_satoshi, split_amount_and_denomination, Denomination, Display, DisplayStyle,
|
||||||
OutOfRangeError, ParseAmountError, ParseError, SignedAmount,
|
OutOfRangeError, ParseAmountError, ParseError, SignedAmount,
|
||||||
|
@ -103,7 +104,9 @@ impl Amount {
|
||||||
let (negative, satoshi) =
|
let (negative, satoshi) =
|
||||||
parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(false))?;
|
parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(false))?;
|
||||||
if negative {
|
if negative {
|
||||||
return Err(ParseAmountError::OutOfRange(OutOfRangeError::negative()));
|
return Err(ParseAmountError(ParseAmountErrorInner::OutOfRange(
|
||||||
|
OutOfRangeError::negative(),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
Ok(Amount::from_sat(satoshi))
|
Ok(Amount::from_sat(satoshi))
|
||||||
}
|
}
|
||||||
|
@ -405,7 +408,7 @@ impl FromStr for Amount {
|
||||||
let result = Amount::from_str_with_denomination(s);
|
let result = Amount::from_str_with_denomination(s);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Err(ParseError::MissingDenomination(_)) => {
|
Err(ParseError(ParseErrorInner::MissingDenomination(_))) => {
|
||||||
let d = Amount::from_str_in(s, Denomination::Satoshi);
|
let d = Amount::from_str_in(s, Denomination::Satoshi);
|
||||||
|
|
||||||
if d == Ok(Amount::ZERO) {
|
if d == Ok(Amount::ZERO) {
|
||||||
|
|
Loading…
Reference in New Issue