use std::fmt;
pub const ENDF_INTEGER_LENGTH: usize = 11;
pub const ENDF_INTEGER_MAX_ABSOLUTE_VALUE: u64 = 9_999_999_999; pub const ENDF_INTEGER_MAX_DIGITS: u32 = 10;
pub const ENDF_INTEGER_RADIX: u32 = 10;
pub const ENDF_INTEGER_REGEX: &str = r"^[ ]*[-+]?\d{1,10}[ ]*$";
#[derive(Debug, Eq, PartialEq)]
pub enum ParseEndfIntegerError {
Empty,
EmptyIntegerPart,
InvalidDigit { invalid: char },
InvalidSign { invalid: char },
NonASCII,
Overflow,
TooLong,
}
impl fmt::Display for ParseEndfIntegerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseEndfIntegerError::Empty => write!(f, "empty string"),
ParseEndfIntegerError::EmptyIntegerPart => write!(f, "empty integer part"),
ParseEndfIntegerError::InvalidDigit { invalid: x } => {
write!(f, "invalid digit: '{}'", x)
}
ParseEndfIntegerError::InvalidSign { invalid: s } => write!(f, "invalid sign '{}'", s),
ParseEndfIntegerError::NonASCII => write!(f, "non ascii string"),
ParseEndfIntegerError::Overflow => write!(f, "endf integer overflow"),
ParseEndfIntegerError::TooLong => write!(f, "too long string"),
}
}
}
pub fn parse(integer: &str) -> Result<i64, ParseEndfIntegerError> {
if !integer.is_ascii() {
return Err(ParseEndfIntegerError::NonASCII);
}
if integer.len() > ENDF_INTEGER_LENGTH {
return Err(ParseEndfIntegerError::TooLong);
}
let bytes = integer.trim().as_bytes();
let (negative, digits) = match bytes.first() {
Some(&b'-') => (true, &bytes[1..]),
Some(&b'+') => (false, &bytes[1..]),
Some(x) if &b'0' <= x && x <= &b'9' => (false, bytes),
Some(x) => {
return Err(ParseEndfIntegerError::InvalidSign {
invalid: *x as char,
})
}
None => return Err(ParseEndfIntegerError::Empty),
};
if digits.is_empty() {
return Err(ParseEndfIntegerError::EmptyIntegerPart);
}
let mut number = 0;
for &digit in digits {
if b'0' <= digit && digit <= b'9' {
number = number * 10 + (digit - b'0') as i64
} else {
return Err(ParseEndfIntegerError::InvalidDigit {
invalid: digit as char,
});
}
}
if number > ENDF_INTEGER_MAX_ABSOLUTE_VALUE as i64 {
return Err(ParseEndfIntegerError::Overflow);
}
if negative {
Ok(-number)
} else {
Ok(number)
}
}
#[cfg(test)]
mod tests {
use super::*;
use regex::Regex;
#[test]
fn parse_empty() {
let integer = "";
let expected = ParseEndfIntegerError::Empty;
let actual = parse(integer).unwrap_err();
assert_eq!(expected, actual);
}
#[test]
fn parse_whitespace() {
let expected = ParseEndfIntegerError::Empty;
assert_eq!(expected, parse(" ").unwrap_err());
assert_eq!(expected, parse("\n").unwrap_err());
assert_eq!(expected, parse("\t").unwrap_err());
assert_eq!(expected, parse("\r").unwrap_err());
assert_eq!(expected, parse(" \n\t\r").unwrap_err());
}
#[test]
fn parse_sign_only() {
let expected = ParseEndfIntegerError::EmptyIntegerPart;
assert_eq!(expected, parse("-").unwrap_err());
assert_eq!(expected, parse("+").unwrap_err());
}
#[test]
fn parse_invalid() {
let integer = "string";
let expected = ParseEndfIntegerError::InvalidSign { invalid: 's' };
assert_eq!(expected, parse(integer).unwrap_err());
}
#[test]
fn parse_invalid_digit() {
let integer = "1.0";
let expected = ParseEndfIntegerError::InvalidDigit { invalid: '.' };
assert_eq!(expected, parse(integer).unwrap_err());
let integer = "1e+25";
let expected = ParseEndfIntegerError::InvalidDigit { invalid: 'e' };
assert_eq!(expected, parse(integer).unwrap_err());
let integer = "1+2";
let expected = ParseEndfIntegerError::InvalidDigit { invalid: '+' };
assert_eq!(expected, parse(integer).unwrap_err());
let integer = "-1 2";
let expected = ParseEndfIntegerError::InvalidDigit { invalid: ' ' };
assert_eq!(expected, parse(integer).unwrap_err());
}
#[test]
fn parse_invalid_sign() {
let integer = "%1234567890";
let expected = ParseEndfIntegerError::InvalidSign { invalid: '%' };
assert_eq!(expected, parse(integer).unwrap_err());
}
#[test]
fn parse_non_ascii() {
let integer = "µ";
let expected = ParseEndfIntegerError::NonASCII;
assert_eq!(expected, parse(integer).unwrap_err());
}
#[test]
fn parse_overflow() {
let integer = "10000000000";
let expected = ParseEndfIntegerError::Overflow;
assert_eq!(expected, parse(integer).unwrap_err());
}
#[test]
fn parse_invalid_length() {
let integer = "+11111111111";
let expected = ParseEndfIntegerError::TooLong;
assert_eq!(expected, parse(integer).unwrap_err());
let integer = "-11111111111";
let expected = ParseEndfIntegerError::TooLong;
assert_eq!(expected, parse(integer).unwrap_err());
}
#[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() {
let regex = Regex::new(ENDF_INTEGER_REGEX).unwrap();
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 "));
assert!(regex.is_match(" +1"));
assert!(regex.is_match(" +1 "));
assert!(regex.is_match("+1 "));
assert!(regex.is_match(" -1"));
assert!(regex.is_match(" -1 "));
assert!(regex.is_match("-1 "));
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"));
}
}