rpn_cli/read/
count.rs

1use num::CheckedSub;
2use std::fmt::{Alignment, Display, Formatter};
3use std::ops::{Add, Sub};
4
5#[derive(Copy, Clone, Debug, PartialEq)]
6pub enum Count {
7    Some(usize),
8    All,
9}
10
11impl Add for Count {
12    type Output = Self;
13
14    fn add(self, rhs: Self) -> Self::Output {
15        match (self, rhs) {
16            (Count::Some(lhs), Count::Some(rhs)) => Count::Some(lhs + rhs),
17            (Count::Some(_), Count::All) => Count::All,
18            (Count::All, _) => Count::All,
19        }
20    }
21}
22
23impl Sub for Count {
24    type Output = Self;
25
26    fn sub(self, rhs: Self) -> Self::Output {
27        match (self, rhs) {
28            (Count::Some(lhs), Count::Some(rhs)) => Count::Some(lhs.checked_sub(rhs).unwrap_or_default()),
29            (Count::Some(_), Count::All) => Count::Some(0),
30            (Count::All, _) => Count::All,
31        }
32    }
33}
34
35impl CheckedSub for Count {
36    fn checked_sub(&self, rhs: &Self) -> Option<Self> {
37        match (self, rhs) {
38            (Count::Some(lhs), Count::Some(rhs)) => lhs.checked_sub(rhs).map(Count::Some),
39            (Count::Some(_), Count::All) => None,
40            (Count::All, _) => Some(Count::All),
41        }
42    }
43}
44
45impl Display for Count {
46    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
47        match self {
48            Self::Some(0) => Display::fmt("", f),
49            Self::Some(1) => Display::fmt("N", f),
50            Self::Some(2) => Display::fmt("N N", f),
51            Self::Some(_) => Display::fmt(format_many(f), f),
52            Self::All => Display::fmt("*", f),
53        }
54    }
55}
56
57fn format_many(f: &Formatter<'_>) -> &'static str {
58    if let Some(Alignment::Right) = f.align() { "..N" } else { "N.." }
59}
60
61#[cfg(test)]
62pub mod tests {
63    use crate::read::count::Count;
64    use num::CheckedSub;
65
66    #[test]
67    fn test_counts_are_added() {
68        assert_eq!(Count::Some(0) + Count::Some(0), Count::Some(0));
69        assert_eq!(Count::Some(0) + Count::Some(1), Count::Some(1));
70        assert_eq!(Count::Some(0) + Count::Some(2), Count::Some(2));
71        assert_eq!(Count::Some(0) + Count::All, Count::All);
72        assert_eq!(Count::Some(1) + Count::Some(0), Count::Some(1));
73        assert_eq!(Count::Some(1) + Count::Some(1), Count::Some(2));
74        assert_eq!(Count::Some(1) + Count::Some(2), Count::Some(3));
75        assert_eq!(Count::Some(1) + Count::All, Count::All);
76        assert_eq!(Count::Some(2) + Count::Some(0), Count::Some(2));
77        assert_eq!(Count::Some(2) + Count::Some(1), Count::Some(3));
78        assert_eq!(Count::Some(2) + Count::Some(2), Count::Some(4));
79        assert_eq!(Count::Some(2) + Count::All, Count::All);
80        assert_eq!(Count::All + Count::Some(0), Count::All);
81        assert_eq!(Count::All + Count::Some(1), Count::All);
82        assert_eq!(Count::All + Count::Some(2), Count::All);
83        assert_eq!(Count::All + Count::All, Count::All);
84    }
85
86    #[test]
87    fn test_counts_are_subtracted_with_floor() {
88        assert_eq!(Count::Some(0) - Count::Some(0), Count::Some(0));
89        assert_eq!(Count::Some(0) - Count::Some(1), Count::Some(0));
90        assert_eq!(Count::Some(0) - Count::Some(2), Count::Some(0));
91        assert_eq!(Count::Some(0) - Count::All, Count::Some(0));
92        assert_eq!(Count::Some(1) - Count::Some(0), Count::Some(1));
93        assert_eq!(Count::Some(1) - Count::Some(1), Count::Some(0));
94        assert_eq!(Count::Some(1) - Count::Some(2), Count::Some(0));
95        assert_eq!(Count::Some(1) - Count::All, Count::Some(0));
96        assert_eq!(Count::Some(2) - Count::Some(0), Count::Some(2));
97        assert_eq!(Count::Some(2) - Count::Some(1), Count::Some(1));
98        assert_eq!(Count::Some(2) - Count::Some(2), Count::Some(0));
99        assert_eq!(Count::Some(2) - Count::All, Count::Some(0));
100        assert_eq!(Count::All - Count::Some(0), Count::All);
101        assert_eq!(Count::All - Count::Some(1), Count::All);
102        assert_eq!(Count::All - Count::Some(2), Count::All);
103        assert_eq!(Count::All - Count::All, Count::All);
104    }
105
106    #[test]
107    fn test_counts_are_subtracted_with_check() {
108        assert_eq!(Count::Some(0).checked_sub(&Count::Some(0)), Some(Count::Some(0)));
109        assert_eq!(Count::Some(0).checked_sub(&Count::Some(1)), None);
110        assert_eq!(Count::Some(0).checked_sub(&Count::Some(2)), None);
111        assert_eq!(Count::Some(0).checked_sub(&Count::All), None);
112        assert_eq!(Count::Some(1).checked_sub(&Count::Some(0)), Some(Count::Some(1)));
113        assert_eq!(Count::Some(1).checked_sub(&Count::Some(1)), Some(Count::Some(0)));
114        assert_eq!(Count::Some(1).checked_sub(&Count::Some(2)), None);
115        assert_eq!(Count::Some(1).checked_sub(&Count::All), None);
116        assert_eq!(Count::Some(2).checked_sub(&Count::Some(0)), Some(Count::Some(2)));
117        assert_eq!(Count::Some(2).checked_sub(&Count::Some(1)), Some(Count::Some(1)));
118        assert_eq!(Count::Some(2).checked_sub(&Count::Some(2)), Some(Count::Some(0)));
119        assert_eq!(Count::Some(2).checked_sub(&Count::All), None);
120        assert_eq!(Count::All.checked_sub(&Count::Some(0)), Some(Count::All));
121        assert_eq!(Count::All.checked_sub(&Count::Some(1)), Some(Count::All));
122        assert_eq!(Count::All.checked_sub(&Count::Some(2)), Some(Count::All));
123        assert_eq!(Count::All.checked_sub(&Count::All), Some(Count::All));
124    }
125
126    #[test]
127    fn test_counts_are_left_justified() {
128        assert_eq!(format!("[{:5}]", Count::Some(0)), "[     ]");
129        assert_eq!(format!("[{:5}]", Count::Some(1)), "[N    ]");
130        assert_eq!(format!("[{:5}]", Count::Some(2)), "[N N  ]");
131        assert_eq!(format!("[{:5}]", Count::Some(3)), "[N..  ]");
132        assert_eq!(format!("[{:5}]", Count::Some(4)), "[N..  ]");
133        assert_eq!(format!("[{:5}]", Count::All), "[*    ]");
134    }
135
136    #[test]
137    fn test_counts_are_right_justified() {
138        assert_eq!(format!("[{:>5}]", Count::Some(0)), "[     ]");
139        assert_eq!(format!("[{:>5}]", Count::Some(1)), "[    N]");
140        assert_eq!(format!("[{:>5}]", Count::Some(2)), "[  N N]");
141        assert_eq!(format!("[{:>5}]", Count::Some(3)), "[  ..N]");
142        assert_eq!(format!("[{:>5}]", Count::Some(4)), "[  ..N]");
143        assert_eq!(format!("[{:>5}]", Count::All), "[    *]");
144    }
145}