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}