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()) } 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 { impl From<InvalidCharacterError> for ParseError {
fn from(e: InvalidCharacterError) -> Self { Self::Amount(e.into()) } fn from(e: InvalidCharacterError) -> Self { Self::Amount(e.into()) }
} }
@ -217,7 +221,7 @@ pub enum ParseAmountError {
/// A digit was expected but not found. /// A digit was expected but not found.
MissingDigits(MissingDigitsError), MissingDigits(MissingDigitsError),
/// Input string was too large. /// Input string was too large.
InputTooLarge, InputTooLarge(InputTooLargeError),
/// Invalid character in input. /// Invalid character in input.
InvalidCharacter(InvalidCharacterError), 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 { impl From<InvalidCharacterError> for ParseAmountError {
fn from(value: InvalidCharacterError) -> Self { fn from(value: InvalidCharacterError) -> Self {
Self::InvalidCharacter(value) Self::InvalidCharacter(value)
@ -242,7 +253,7 @@ impl fmt::Display for ParseAmountError {
OutOfRange(ref error) => write_err!(f, "amount out of range"; error), OutOfRange(ref error) => write_err!(f, "amount out of range"; error),
TooPrecise => f.write_str("amount has a too high precision"), TooPrecise => f.write_str("amount has a too high precision"),
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),
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), InvalidCharacter(ref error) => write_err!(f, "invalid character in the input"; error),
} }
} }
@ -254,7 +265,8 @@ impl std::error::Error for ParseAmountError {
use ParseAmountError::*; use ParseAmountError::*;
match *self { match *self {
TooPrecise | InputTooLarge => None, TooPrecise => None,
InputTooLarge(ref error) => Some(error),
OutOfRange(ref error) => Some(error), OutOfRange(ref error) => Some(error),
MissingDigits(ref error) => Some(error), MissingDigits(ref error) => Some(error),
InvalidCharacter(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. /// 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. /// 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 /// Parse decimal string in the given denomination into a satoshi value and a
/// bool indicator for a negative amount. /// bool indicator for a negative amount.
fn parse_signed_to_satoshi( fn parse_signed_to_satoshi(
@ -466,8 +498,8 @@ fn parse_signed_to_satoshi(
if s.is_empty() { if s.is_empty() {
return Err(InnerParseError::MissingDigits(MissingDigitsError { kind: MissingDigitsKind::Empty })); return Err(InnerParseError::MissingDigits(MissingDigitsError { kind: MissingDigitsKind::Empty }));
} }
if s.len() > 50 { if s.len() > INPUT_STRING_LEN_LIMIT {
return Err(InnerParseError::InputTooLarge); return Err(InnerParseError::InputTooLarge(s.len()));
} }
let is_negative = s.starts_with('-'); let is_negative = s.starts_with('-');
@ -547,7 +579,7 @@ enum InnerParseError {
Overflow { is_negative: bool }, Overflow { is_negative: bool },
TooPrecise, TooPrecise,
MissingDigits(MissingDigitsError), MissingDigits(MissingDigitsError),
InputTooLarge, InputTooLarge(usize),
InvalidCharacter(InvalidCharacterError), InvalidCharacter(InvalidCharacterError),
} }
@ -557,7 +589,7 @@ impl InnerParseError {
Self::Overflow { is_negative } => OutOfRangeError { is_signed, is_greater_than_max: !is_negative }.into(), Self::Overflow { is_negative } => OutOfRangeError { is_signed, is_greater_than_max: !is_negative }.into(),
Self::TooPrecise => ParseAmountError::TooPrecise, Self::TooPrecise => ParseAmountError::TooPrecise,
Self::MissingDigits(error) => ParseAmountError::MissingDigits(error), Self::MissingDigits(error) => ParseAmountError::MissingDigits(error),
Self::InputTooLarge => ParseAmountError::InputTooLarge, Self::InputTooLarge(len) => ParseAmountError::InputTooLarge(InputTooLargeError { len }),
Self::InvalidCharacter(error) => ParseAmountError::InvalidCharacter(error), Self::InvalidCharacter(error) => ParseAmountError::InvalidCharacter(error),
} }
} }
@ -2177,7 +2209,7 @@ mod tests {
// 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) Err(E::InputTooLarge(InputTooLargeError { len: 51 }))
); );
} }