use std::error::Error;
use std::fmt::{Display, Formatter};
pub const ENDF_INT_ABS_MAX: i64 = 9_999_999_999;
pub const ENDF_INT_MAX_LEN: usize = 11;
pub const ENDF_INT_RADIX: i64 = 10;
pub const ENDF_INT_REGEX: &str = r"^[ ]*[-+]?(?:0|[1-9]\d{0,9}?)$";
#[derive(Debug)]
pub struct ParseEndfIntError {
kind: ParseEndfIntErrorKind,
}
impl ParseEndfIntError {
pub fn kind(&self) -> ParseEndfIntErrorKind {
self.kind
}
}
impl Display for ParseEndfIntError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.kind() {
ParseEndfIntErrorKind::Empty => {
write!(f, "cannot parse ENDF integer from empty string")
}
ParseEndfIntErrorKind::EmptyIntegerPart => {
write!(f, "cannot parse ENDF integer without integer part")
}
ParseEndfIntErrorKind::InvalidDigit(c) => write!(
f,
"cannot parse ENDF integer, invalid digit found in string: '{}'",
c.escape_default().collect::<String>()
),
ParseEndfIntErrorKind::NonAscii => {
write!(f, "cannot parse ENDF integer from non ASCII string")
}
ParseEndfIntErrorKind::Overflow => {
write!(f, "cannot parse ENDF integer, number too large")
}
ParseEndfIntErrorKind::TooLong => {
write!(f, "cannot parse ENDF integer, string too long")
}
}
}
}
impl Error for ParseEndfIntError {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum ParseEndfIntErrorKind {
Empty,
EmptyIntegerPart,
InvalidDigit(char),
NonAscii,
Overflow,
TooLong,
}
pub fn parse(integer: &str) -> Result<i64, ParseEndfIntError> {
if !integer.is_ascii() {
return Err(ParseEndfIntError {
kind: ParseEndfIntErrorKind::NonAscii,
});
}
let bytes = integer.trim().as_bytes();
if bytes.len() > ENDF_INT_MAX_LEN {
return Err(ParseEndfIntError {
kind: ParseEndfIntErrorKind::TooLong,
});
}
let (neg, digits) = match bytes.first() {
Some(&b'-') => (true, &bytes[1..]),
Some(&b'+') => (false, &bytes[1..]),
Some(_) => (false, bytes),
None => {
return Err(ParseEndfIntError {
kind: ParseEndfIntErrorKind::Empty,
})
}
};
if digits.is_empty() {
return Err(ParseEndfIntError {
kind: ParseEndfIntErrorKind::EmptyIntegerPart,
});
}
let mut number: i64 = 0;
for &digit in digits {
if digit >= b'0' && digit <= b'9' {
number = ENDF_INT_RADIX * number + ((digit - b'0') as i64)
} else {
return Err(ParseEndfIntError {
kind: ParseEndfIntErrorKind::InvalidDigit(digit as char),
});
}
}
if number > ENDF_INT_ABS_MAX {
return Err(ParseEndfIntError {
kind: ParseEndfIntErrorKind::Overflow,
});
}
if neg {
Ok(-number)
} else {
Ok(number)
}
}
#[cfg(test)]
mod tests {
use super::*;
use regex::Regex;
#[test]
fn parse_empty() {
let integer = "";
let expected = ParseEndfIntErrorKind::Empty;
let actual = parse(integer).unwrap_err();
assert_eq!(expected, actual.kind());
}
#[test]
fn parse_whitespace() {
let expected = ParseEndfIntErrorKind::Empty;
assert_eq!(expected, parse(" ").unwrap_err().kind());
assert_eq!(expected, parse("\t").unwrap_err().kind());
assert_eq!(expected, parse("\n").unwrap_err().kind());
assert_eq!(expected, parse("\r").unwrap_err().kind());
assert_eq!(expected, parse(" \n\t\r").unwrap_err().kind());
}
#[test]
fn parse_sign_only() {
let expected = ParseEndfIntErrorKind::EmptyIntegerPart;
assert_eq!(expected, parse("-").unwrap_err().kind());
assert_eq!(expected, parse("+").unwrap_err().kind());
}
#[test]
fn parse_invalid() {
let integer = "string";
let expected = ParseEndfIntErrorKind::InvalidDigit('s');
assert_eq!(expected, parse(integer).unwrap_err().kind());
}
#[test]
fn parse_invalid_digit() {
let integer = "1.0";
let expected = ParseEndfIntErrorKind::InvalidDigit('.');
assert_eq!(expected, parse(integer).unwrap_err().kind());
let integer = "1e+25";
let expected = ParseEndfIntErrorKind::InvalidDigit('e');
assert_eq!(expected, parse(integer).unwrap_err().kind());
let integer = "1+2";
let expected = ParseEndfIntErrorKind::InvalidDigit('+');
assert_eq!(expected, parse(integer).unwrap_err().kind());
let integer = "-1 2";
let expected = ParseEndfIntErrorKind::InvalidDigit(' ');
assert_eq!(expected, parse(integer).unwrap_err().kind());
}
#[test]
fn parse_invalid_sign() {
let integer = "%1234567890";
let expected = ParseEndfIntErrorKind::InvalidDigit('%');
assert_eq!(expected, parse(integer).unwrap_err().kind());
}
#[test]
fn parse_non_ascii() {
let integer = "µ";
let expected = ParseEndfIntErrorKind::NonAscii;
assert_eq!(expected, parse(integer).unwrap_err().kind());
}
#[test]
fn parse_overflow() {
let integer = "10000000000";
let expected = ParseEndfIntErrorKind::Overflow;
assert_eq!(expected, parse(integer).unwrap_err().kind());
}
#[test]
fn parse_too_long() {
let integer = "-11111111111";
let expected = ParseEndfIntErrorKind::TooLong;
assert_eq!(expected, parse(integer).unwrap_err().kind());
}
#[test]
fn parse_valid_near_zero() {
assert_eq!(0, parse("0").unwrap());
assert_eq!(1, parse("1").unwrap());
assert_eq!(-1, parse("-1").unwrap());
assert_eq!(1, parse("+1").unwrap());
}
#[test]
fn parse_valid_limits() {
assert_eq!(9_999_999_999, parse("9999999999").unwrap());
assert_eq!(9_999_999_999, parse("+9999999999").unwrap());
assert_eq!(9_999_999_999, parse(" 9999999999").unwrap());
assert_eq!(-9_999_999_999, parse("-9999999999").unwrap());
}
#[test]
fn parse_valid_positive_unsigned() {
assert_eq!(1, parse(" 1").unwrap());
assert_eq!(12, parse(" 12").unwrap());
assert_eq!(123, parse(" 123").unwrap());
assert_eq!(1234, parse(" 1234").unwrap());
assert_eq!(12345, parse(" 12345").unwrap());
assert_eq!(123_456, parse(" 123456").unwrap());
assert_eq!(1_234_567, parse(" 1234567").unwrap());
assert_eq!(12_345_678, parse(" 12345678").unwrap());
assert_eq!(123_456_789, parse(" 123456789").unwrap());
assert_eq!(1_234_567_890, parse(" 1234567890").unwrap());
}
#[test]
fn parse_valid_positive_signed() {
assert_eq!(1, parse(" +1").unwrap());
assert_eq!(12, parse(" +12").unwrap());
assert_eq!(123, parse(" +123").unwrap());
assert_eq!(1234, parse(" +1234").unwrap());
assert_eq!(12345, parse(" +12345").unwrap());
assert_eq!(123_456, parse(" +123456").unwrap());
assert_eq!(1_234_567, parse(" +1234567").unwrap());
assert_eq!(12_345_678, parse(" +12345678").unwrap());
assert_eq!(123_456_789, parse(" +123456789").unwrap());
assert_eq!(1_234_567_890, parse("+1234567890").unwrap());
}
#[test]
fn parse_valid_negative_signed() {
assert_eq!(-1, parse(" -1").unwrap());
assert_eq!(-12, parse(" -12").unwrap());
assert_eq!(-123, parse(" -123").unwrap());
assert_eq!(-1234, parse(" -1234").unwrap());
assert_eq!(-12345, parse(" -12345").unwrap());
assert_eq!(-123_456, parse(" -123456").unwrap());
assert_eq!(-1_234_567, parse(" -1234567").unwrap());
assert_eq!(-12_345_678, parse(" -12345678").unwrap());
assert_eq!(-123_456_789, parse(" -123456789").unwrap());
assert_eq!(-1_234_567_890, parse("-1234567890").unwrap());
}
#[test]
fn parse_valid_zero_padded() {
assert_eq!(1, parse("00000000001").unwrap());
assert_eq!(12, parse("00000000012").unwrap());
assert_eq!(123, parse("00000000123").unwrap());
assert_eq!(1234, parse("00000001234").unwrap());
assert_eq!(12345, parse("00000012345").unwrap());
assert_eq!(123_456, parse("00000123456").unwrap());
assert_eq!(1_234_567, parse("00001234567").unwrap());
assert_eq!(12_345_678, parse("00012345678").unwrap());
assert_eq!(123_456_789, parse("00123456789").unwrap());
assert_eq!(1_234_567_890, parse("01234567890").unwrap());
}
#[test]
fn regex_valid() {
let regex = Regex::new(ENDF_INT_REGEX).unwrap();
assert!(regex.is_match("0"));
assert!(regex.is_match(" 0"));
assert!(regex.is_match(" -0"));
assert!(regex.is_match("1"));
assert!(regex.is_match("+1"));
assert!(regex.is_match("-1"));
assert!(regex.is_match(" 1234567890"));
assert!(regex.is_match("-1234567890"));
assert!(regex.is_match("+1234567890"));
assert!(regex.is_match(" 1"));
assert!(regex.is_match(" +1"));
assert!(regex.is_match(" -1"));
}
#[test]
fn regex_invalid() {
let regex = Regex::new(ENDF_INT_REGEX).unwrap();
assert!(!regex.is_match("12345678901")); assert!(!regex.is_match("-")); assert!(!regex.is_match("+")); assert!(!regex.is_match("1.23456789")); assert!(!regex.is_match("-1.2345e+1")); assert!(!regex.is_match("-1.2345e-1")); assert!(!regex.is_match("invalid")); assert!(!regex.is_match("+ 1")); assert!(!regex.is_match("- 1")); }
}