miv 0.1.0

A crate for working with Roman numerals
Documentation
use miv::*;

#[test]
fn valid_numbers_are_parsed_correctly() {
    assert_valid_num("I", 1);
    assert_valid_num("II", 2);
    assert_valid_num("III", 3);
    assert_valid_num("IV", 4);
    assert_valid_num("V", 5);
    assert_valid_num("VI", 6);
    assert_valid_num("VII", 7);
    assert_valid_num("VIII", 8);
    assert_valid_num("IX", 9);
    assert_valid_num("X", 10);
    assert_valid_num("XVII", 17);
    assert_valid_num("XXXIV", 34);
    assert_valid_num("XXXIX", 39);
    assert_valid_num("XL", 40);
    assert_valid_num("XLIX", 49);
    assert_valid_num("L", 50);
    assert_valid_num("LXXIX", 79);
    assert_valid_num("XC", 90);
    assert_valid_num("XCV", 95);
    assert_valid_num("C", 100);
    assert_valid_num("CXV", 115);
    assert_valid_num("CLII", 152);
    assert_valid_num("CXCVII", 197);
    assert_valid_num("CCLIX", 259);
    assert_valid_num("CD", 400);
    assert_valid_num("CDLXXVII", 477);
    assert_valid_num("D", 500);
    assert_valid_num("DCCCLI", 851);
    assert_valid_num("CM", 900);
    assert_valid_num("CMIX", 909);
    assert_valid_num("M", 1000);
    assert_valid_num("MMCDXXI", 2421);
    assert_valid_num("MMMCMXCIX", 3999);

}

#[test]
fn non_roman_strings_are_invalid() {
    assert_empty_string("");
    assert_invalid_character("aaa");
    assert_invalid_character("hello");
    assert_invalid_character("xii");
    assert_invalid_character("іваіллуш");
    assert_invalid_character("QXUI");
    assert_invalid_character("KDE");
}


#[test]
fn smaller_numbers_cannot_precede_larger_ones() {
    assert_invalid_sequence("XXCD");
    assert_invalid_sequence("IIVX");
    assert_invalid_sequence("VX");
    assert_invalid_sequence("VXII");
    assert_invalid_sequence("VXII");
}

#[test]
fn some_numbers_are_not_allowed_to_come_one_after_the_other(){
    assert_invalid_sequence("VV");
    assert_invalid_sequence("IXIX");
    assert_invalid_sequence("IVIV");
    assert_invalid_sequence("XCXC");
    assert_invalid_sequence("XCL");
    assert_invalid_sequence("LXLVII");
    assert_invalid_sequence("DCDV");
    assert_invalid_sequence("CXXIXV");
    assert_invalid_sequence("IVI");
    assert_invalid_sequence("IXIV");
    assert_invalid_sequence("XCXVI");
    assert_invalid_sequence("VIV");
    assert_invalid_sequence("DCD");
    assert_invalid_sequence("CMD");
    assert_invalid_sequence("CDC");
    assert_invalid_sequence("XCL");
}

#[test]
fn over_three_same_numbers_in_a_row_are_invalid() {
    assert_too_many_repetitions("MMMDXXXXXXX");
    assert_too_many_repetitions("VIIII");
    assert_too_many_repetitions("CCCC");
}

#[test]
fn numbers_are_converted_into_valid_roman_strings() {
    assert_string_from_number(2, "II");
    assert_string_from_number(5, "V");
    assert_string_from_number(9, "IX");
    assert_string_from_number(15, "XV");
    assert_string_from_number(19, "XIX");
    assert_string_from_number(37, "XXXVII");
    assert_string_from_number(40, "XL");
    assert_string_from_number(53, "LIII");
    assert_string_from_number(60, "LX");
    assert_string_from_number(99, "XCIX");
    assert_string_from_number(128, "CXXVIII");
}

#[test]
fn roman_struct_cannot_be_constructed_from_zero() {
    assert_error(Roman::from_integer(0), Error::NumberZero);
}

#[test]
fn the_largest_valid_number_is_3999() {
    let roman = Roman::from_integer(3999);
    assert!(roman.is_ok());

    assert_error(Roman::from_integer(4000), Error::NumberTooLarge);
}

fn assert_invalid_sequence(s: &str) {
    let res = Roman::from_string(s);
    assert!(res.is_err());
    assert!({
        if let Error::InvalidSequence(_, _) = res.unwrap_err()  {
            true
        } else {
            false
        }
    });
}

fn assert_too_many_repetitions(s: &str) {
    assert_error(Roman::from_string(s), Error::TooManyRepetitions);
}

fn assert_empty_string(s: &str) {
    assert_error(Roman::from_string(s), Error::EmptyString);
}

fn assert_invalid_character(s: &str) {
    let res = Roman::from_string(s);
    assert!(res.is_err());
    assert!({
        if let Error::InvalidCharacter(_) = res.unwrap_err()  {
            true
        } else {
            false
        }
    });
}

fn assert_valid_num(s: &str, value: u16) {
    assert_eq!(Roman::from_string(s).unwrap().as_int(), value); 
}

fn assert_string_from_number(n: u16, value: &str) {
    let result = Roman::from_integer(n);
    assert!(result.is_ok());

    let roman_string = result.unwrap().as_string();
    assert_eq!(roman_string, String::from(value));
}

fn assert_error(res: Result<Roman>, err: Error) {
    assert!(res.is_err());
    assert_eq!(res.unwrap_err(), err);
}