apple_clis/shared/identifiers/generation/
num_generation.rs1use std::hash::Hash;
2
3use crate::prelude::*;
4
5#[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord)]
7pub struct NumGeneration {
8 num: NonZeroU8,
9 short: bool,
10}
11
12impl PartialEq for NumGeneration {
13 fn eq(&self, other: &Self) -> bool {
14 self.num == other.num
15 }
16}
17
18impl Hash for NumGeneration {
19 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
20 self.num.hash(state);
21 }
22}
23
24impl NumGeneration {
25 const fn ordinal(&self) -> &str {
26 match self.num.get() {
27 1 => "st",
28 2 => "nd",
29 3 => "rd",
30 _ => "th",
31 }
32 }
33
34 #[cfg_attr(not(test), allow(dead_code))]
35 pub(super) fn testing_new(num: NonZeroU8) -> Self {
36 Self {
37 num,
38 short: false,
39 }
40 }
41
42 fn long(num: NonZeroU8) -> Self {
43 Self {
44 num,
45 short: false,
46 }
47 }
48
49 fn short(num: NonZeroU8) -> Self {
50 Self {
51 num,
52 short: true,
53 }
54 }
55
56 pub fn get(&self) -> u8 {
57 self.num.get()
58 }
59}
60
61#[tracing::instrument(level = "trace", skip(input))]
62fn ordinal(input: &str) -> IResult<&str, &str> {
63 alt((tag("st"), tag("nd"), tag("rd"), tag("th")))(input)
64}
65
66fn generation_brackets(input: &str) -> IResult<&str, NumGeneration> {
67 delimited(
68 ws(tag("(")),
69 map(NonZeroU8::nom_from_str, NumGeneration::long),
70 preceded(ws(ordinal), tag("generation)")),
71 )(input)
72}
73
74fn generation_model(input: &str) -> IResult<&str, NumGeneration> {
75 terminated(
76 map(NonZeroU8::nom_from_str, NumGeneration::short),
77 tag("G"),
78 )(input)
79}
80
81impl NomFromStr for NumGeneration {
82 #[tracing::instrument(level = "trace", skip(input))]
83 fn nom_from_str(input: &str) -> IResult<&str, Self> {
84 alt((generation_brackets, generation_model))(input)
85 }
86}
87
88impl Display for NumGeneration {
89 #[tracing::instrument(level = "trace", skip(self, f))]
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 match self.short {
92 false => write!(f, "({}{} generation)", self.get(), self.ordinal()),
93 true => write!(f, "{}G", self.get()),
94 }
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use tracing::debug;
101
102 use super::*;
103
104 #[test]
105 fn generation_ordering() {
106 let old = NumGeneration::long(NonZeroU8::new(1).unwrap());
107 let newer = NumGeneration::short(NonZeroU8::new(2).unwrap());
108 assert!(newer > old);
109
110 let old = NumGeneration::short(NonZeroU8::new(1).unwrap());
111 let newer = NumGeneration::long(NonZeroU8::new(2).unwrap());
112 assert!(newer > old);
113 }
114
115 #[test]
116 fn test_parse_ordinal() {
117 let examples = ["st", "nd", "th"];
118 for example in examples.iter() {
119 let output = ordinal(example);
120 match output {
121 Ok((remaining, _)) => {
122 debug!("Parsed ordinal from {}: {:?}", example, remaining)
123 }
124 Err(e) => panic!("Failed to parse {:?}: {}", example, e),
125 }
126 }
127 }
128
129 #[test]
130 fn hardcoded_num_generation() {
131 let examples = [
132 "(1st generation)",
133 "(2nd generation)",
134 "(3rd generation)",
135 "(4th generation)",
136 "3G",
137 "69G",
138 ];
139 for example in examples.iter() {
140 let output = NumGeneration::nom_from_str(example);
141 match output {
142 Ok((remaining, generation)) => {
143 debug!(
144 "Parsed generation: {:?} from {} [remaining: {}]",
145 generation, example, remaining
146 );
147 assert!(
148 remaining.is_empty(),
149 "Remaining was not empty: {}",
150 remaining
151 );
152 assert_eq!(&format!("{}", generation), example);
153 }
154 Err(e) => panic!("Failed to parse {:?}: {}", example, e),
155 }
156 }
157 }
158}