use std::str::{FromStr};
use std::fmt::{self, Display, Formatter};
use num::{BigUint, Num, One, ToPrimitive, Zero};
#[derive(Debug,Eq,PartialEq)]
pub struct RF {
data: String,
}
impl RF {
pub fn calculate_check(&self) -> u8 {
let data = self.calculate_int();
let digits = (BigUint::from(100u32) * data - BigUint::one()) % BigUint::from(97u32);
97 - digits.to_u8().unwrap()
}
fn calculate_int(&self) -> BigUint {
let mut data = BigUint::zero();
for c in self.data.bytes() {
match c {
b'0'...b'9' => {
data = data * BigUint::from(10u32) + BigUint::from(c - b'0');
},
b'A'...b'Z' => {
data = data * BigUint::from(100u32) + BigUint::from(c - b'A' + 10);
}
_ => unreachable!("RF data structure contains invalid character"),
}
}
data = data * BigUint::from(100u32) + BigUint::from(27u32);
data = data * BigUint::from(100u32) + BigUint::from(15u32);
data
}
pub fn new(input: &str) -> Result<RF, &'static str> {
let input = input.trim().replace(' ', "");
if input.len() > 21 {
return Err("Input too long")
}
if input.len() == 0 {
return Err("Input too short")
}
let input = input.to_uppercase();
let valid_data = input.chars().all(|x| (x >= 'A' && x <= 'Z') || (x >= '0' && x <= '9') );
if !valid_data {
return Err("Invalid input character");
}
let rf = RF {
data: input,
};
Ok(rf)
}
}
impl FromStr for RF {
type Err = &'static str;
fn from_str(input: &str) -> Result<RF, Self::Err> {
if input.len() < 5 {
return Err("Formatted RF should be at least 5 characters long");
}
if ! input.starts_with("RF") {
return Err("Input does not start with RF");
}
let rf = RF::new(&input[4..])?;
let check = input[2..4].parse().map_err(|_| "Cannot parse RF check digits.")?;
if rf.calculate_check() != check {
return Err("Check digits are incorrect.");
}
Ok(rf)
}
}
impl Display for RF {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
let formatted = format!("{}", self.data);
let first_group_length = formatted.len() % 4;
let mut i = first_group_length;
let mut new_formatted = String::with_capacity(formatted.len() + 1 + formatted.len() / 4);
new_formatted.push_str(&formatted[0..first_group_length]);
while i < formatted.len() {
new_formatted.push(' ');
new_formatted.push_str(&formatted[i..i+4]);
i += 4;
}
write!(f, "RF{:02} {}", self.calculate_check(), new_formatted.trim())
}
}
#[test]
fn test_check_digits() {
let tests = vec![
("RF45G72UUR", true),
("RF6518K5", true),
("RF35C4", false),
("RF214377", false),
("RF18 5390 0754 7034", true),
];
for (input, output) in tests {
let parsed = input.parse::<RF>();
assert_eq!(parsed.is_ok(), output, "{}", input)
}
}
#[test]
fn test_parse_and_back() {
let tests = vec![
"RF74 1",
"RF53 F43",
"RF49 F4XX",
];
for input in tests {
let parsed = input.parse::<RF>().unwrap();
assert_eq!(input, parsed.to_string());
}
}