roman_int/
lib.rs

1use std::fmt::Display;
2
3use num_traits::AsPrimitive;
4
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6/// Wrapper around a string, which is the converted numeral
7///
8/// Integer input range is 1..=3999
9///
10/// Use [From<T>](https://doc.rust-lang.org/std/convert/trait.From.html)
11/// and [Into<T>](https://doc.rust-lang.org/std/convert/trait.Into.html)
12/// to convert from and to an integer type.
13pub struct Numeral(String);
14
15impl Display for Numeral {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        write!(f, "{}", self.0)
18    }
19}
20
21impl<T: AsPrimitive<usize>> From<T> for Numeral {
22    fn from(num: T) -> Self {
23        let mut n: usize = num.as_();
24
25        assert!(n < 4000);
26
27        let mut string = String::new();
28
29        fn digit(n: usize, one: char, five: char, ten: char) -> String {
30            match n {
31                0 => String::new(),
32                1 => format!("{0}", one),
33                2 => format!("{0}{0}", one),
34                3 => format!("{0}{0}{0}", one),
35                4 => format!("{0}{1}", one, five),
36                5 => format!("{0}", five),
37                6 => format!("{1}{0}", one, five),
38                7 => format!("{1}{0}{0}", one, five),
39                8 => format!("{1}{0}{0}{0}", one, five),
40                9 => format!("{0}{1}", one, ten),
41                _ => panic!(),
42            }
43        }
44
45        string = digit(n % 10, 'I', 'V', 'X') + &string;
46        n /= 10;
47
48        string = digit(n % 10, 'X', 'L', 'C') + &string;
49        n /= 10;
50
51        string = digit(n % 10, 'C', 'D', 'M') + &string;
52        n /= 10;
53
54        string = match n % 10 {
55            0 => String::new(),
56            1 => format!("{0}", 'M'),
57            2 => format!("{0}{0}", 'M'),
58            3 => format!("{0}{0}{0}", 'M'),
59            _ => panic!(),
60        } + &string;
61
62        Self(string)
63    }
64}