Improve `ParseAmountError::InputTooLarge`

This variant lacked pretty important information: what is the limit. We
could just write it in the error message but it's even better to move it
to a separate struct which also says how many characters exceed the
limit - this helps users know how many need to be deleted.
This commit is contained in:
Martin Habovstiak 2024-02-19 14:01:11 +01:00
parent b7689a7d60
commit 28d83551eb
1 changed files with 40 additions and 8 deletions

View File

@ -177,6 +177,10 @@ impl From<MissingDigitsError> for ParseError {
fn from(e: MissingDigitsError) -> Self { Self::Amount(e.into()) }
}
impl From<InputTooLargeError> for ParseError {
fn from(e: InputTooLargeError) -> Self { Self::Amount(e.into()) }
}
impl From<InvalidCharacterError> for ParseError {
fn from(e: InvalidCharacterError) -> Self { Self::Amount(e.into()) }
}
@ -217,7 +221,7 @@ pub enum ParseAmountError {
/// A digit was expected but not found.
MissingDigits(MissingDigitsError),
/// Input string was too large.
InputTooLarge,
InputTooLarge(InputTooLargeError),
/// Invalid character in input.
InvalidCharacter(InvalidCharacterError),
}
@ -228,6 +232,13 @@ impl From<MissingDigitsError> for ParseAmountError {
}
}
impl From<InputTooLargeError> for ParseAmountError {
fn from(value: InputTooLargeError) -> Self {
Self::InputTooLarge(value)
}
}
impl From<InvalidCharacterError> for ParseAmountError {
fn from(value: InvalidCharacterError) -> Self {
Self::InvalidCharacter(value)
@ -242,7 +253,7 @@ impl fmt::Display for ParseAmountError {
OutOfRange(ref error) => write_err!(f, "amount out of range"; error),
TooPrecise => f.write_str("amount has a too high precision"),
MissingDigits(ref error) => write_err!(f, "the input has too few digits"; error),
InputTooLarge => f.write_str("input string was too large"),
InputTooLarge(ref error) => write_err!(f, "the input is too large"; error),
InvalidCharacter(ref error) => write_err!(f, "invalid character in the input"; error),
}
}
@ -254,7 +265,8 @@ impl std::error::Error for ParseAmountError {
use ParseAmountError::*;
match *self {
TooPrecise | InputTooLarge => None,
TooPrecise => None,
InputTooLarge(ref error) => Some(error),
OutOfRange(ref error) => Some(error),
MissingDigits(ref error) => Some(error),
InvalidCharacter(ref error) => Some(error),
@ -333,6 +345,24 @@ impl From<OutOfRangeError> for ParseAmountError {
}
}
/// Error returned when the input string is too large.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct InputTooLargeError {
len: usize,
}
impl fmt::Display for InputTooLargeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.len - INPUT_STRING_LEN_LIMIT {
1 => write!(f, "the input is one character longer than the maximum allowed length ({})", INPUT_STRING_LEN_LIMIT),
n => write!(f, "the input is {} characters longer than the maximum allowed length ({})", n, INPUT_STRING_LEN_LIMIT),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for InputTooLargeError {}
/// Error returned when digits were expected in the input but there were none.
///
/// In particular, this is currently returned when the string is empty or only contains the minus sign.
@ -457,6 +487,8 @@ fn is_too_precise(s: &str, precision: usize) -> bool {
}
}
const INPUT_STRING_LEN_LIMIT: usize = 50;
/// Parse decimal string in the given denomination into a satoshi value and a
/// bool indicator for a negative amount.
fn parse_signed_to_satoshi(
@ -466,8 +498,8 @@ fn parse_signed_to_satoshi(
if s.is_empty() {
return Err(InnerParseError::MissingDigits(MissingDigitsError { kind: MissingDigitsKind::Empty }));
}
if s.len() > 50 {
return Err(InnerParseError::InputTooLarge);
if s.len() > INPUT_STRING_LEN_LIMIT {
return Err(InnerParseError::InputTooLarge(s.len()));
}
let is_negative = s.starts_with('-');
@ -547,7 +579,7 @@ enum InnerParseError {
Overflow { is_negative: bool },
TooPrecise,
MissingDigits(MissingDigitsError),
InputTooLarge,
InputTooLarge(usize),
InvalidCharacter(InvalidCharacterError),
}
@ -557,7 +589,7 @@ impl InnerParseError {
Self::Overflow { is_negative } => OutOfRangeError { is_signed, is_greater_than_max: !is_negative }.into(),
Self::TooPrecise => ParseAmountError::TooPrecise,
Self::MissingDigits(error) => ParseAmountError::MissingDigits(error),
Self::InputTooLarge => ParseAmountError::InputTooLarge,
Self::InputTooLarge(len) => ParseAmountError::InputTooLarge(InputTooLargeError { len }),
Self::InvalidCharacter(error) => ParseAmountError::InvalidCharacter(error),
}
}
@ -2177,7 +2209,7 @@ mod tests {
// more than 50 chars.
assert_eq!(
p("100000000000000.00000000000000000000000000000000000", Denomination::Bitcoin),
Err(E::InputTooLarge)
Err(E::InputTooLarge(InputTooLargeError { len: 51 }))
);
}