use nom::{
IResult, Parser,
bytes::complete::tag,
character::complete::{char, digit1, hex_digit1},
combinator::opt,
};
pub(super) fn parse_decimal_number(input: &str) -> IResult<&str, i64> {
let (input, digits) = digit1(input)?;
if digits.len() > 19 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::MapRes,
)));
}
let number = digits.parse::<i64>().map_err(|_| {
nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::MapRes))
})?;
Ok((input, number))
}
pub(super) fn parse_unsigned_decimal_number(input: &str) -> IResult<&str, u64> {
let (input, digits) = digit1(input)?;
if digits.len() > 20 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::MapRes,
)));
}
let number = digits.parse::<u64>().map_err(|_| {
nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::MapRes))
})?;
Ok((input, number))
}
pub(super) fn parse_hex_number(input: &str) -> IResult<&str, i64> {
let (input, _) = tag("0x")(input)?;
let (input, hex_str) = hex_digit1(input)?;
if hex_str.len() > 16 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::MapRes,
)));
}
let number = i64::from_str_radix(hex_str, 16).map_err(|_| {
nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::MapRes))
})?;
Ok((input, number))
}
pub(super) fn parse_unsigned_hex_number(input: &str) -> IResult<&str, u64> {
let (input, _) = tag("0x")(input)?;
let (input, hex_str) = hex_digit1(input)?;
if hex_str.len() > 16 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::MapRes,
)));
}
let number = u64::from_str_radix(hex_str, 16).map_err(|_| {
nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::MapRes))
})?;
Ok((input, number))
}
pub(super) fn parse_unsigned_number(input: &str) -> IResult<&str, u64> {
if input.starts_with("0x") {
parse_unsigned_hex_number(input)
} else {
parse_unsigned_decimal_number(input)
}
}
pub fn parse_number(input: &str) -> IResult<&str, i64> {
let (input, sign) = opt(char('-')).parse(input)?;
let is_negative = sign.is_some();
let (input, number) = if input.starts_with("0x") {
parse_hex_number(input)?
} else {
parse_decimal_number(input)?
};
let result = if is_negative {
number.checked_neg().ok_or_else(|| {
nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::MapRes))
})?
} else {
number
};
Ok((input, result))
}