1use crate::{SwitchCommon, SwitchShape, Switches};
2use std::collections::{HashMap, HashSet};
3use std::fmt::{self, Display};
4
5#[derive(PartialEq, Eq, Debug, Default)]
6pub struct Invalid {
7 pub duplicate_shorts: Vec<String>,
8 pub duplicate_longs: Vec<String>,
9 pub one_char_longs: Vec<String>,
10 pub multi_char_shorts: Vec<String>,
11 pub has_empty_switch: bool,
12}
13
14impl Invalid {
15 fn new(
16 duplicate_shorts: Vec<String>,
17 duplicate_longs: Vec<String>,
18 one_char_longs: Vec<String>,
19 multi_char_shorts: Vec<String>,
20 has_empty_switch: bool,
21 ) -> Option<Self> {
22 if duplicate_shorts.is_empty()
23 && duplicate_longs.is_empty()
24 && one_char_longs.is_empty()
25 && multi_char_shorts.is_empty()
26 && !has_empty_switch
27 {
28 None
29 } else {
30 Some(Self {
31 duplicate_shorts,
32 duplicate_longs,
33 one_char_longs,
34 multi_char_shorts,
35 has_empty_switch,
36 })
37 }
38 }
39}
40
41impl Display for Invalid {
42 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
43 assert!(
44 !(self.duplicate_shorts.is_empty()
45 && self.duplicate_longs.is_empty()
46 && self.one_char_longs.is_empty()
47 && self.multi_char_shorts.is_empty()
48 && !self.has_empty_switch)
49 );
50
51 write!(f, "Invalid argument spec!\n\n")?;
52 if !self.duplicate_shorts.is_empty() {
53 write!(f, "Duplicate shorts: \n{:#?}", self.duplicate_shorts)?;
54 }
55 if !self.duplicate_longs.is_empty() {
56 write!(f, "Duplicate longs: \n{:#?}", self.duplicate_longs)?;
57 }
58 if !self.one_char_longs.is_empty() {
59 write!(f, "Single-character longs: \n{:#?}", self.one_char_longs)?;
60 }
61 if !self.multi_char_shorts.is_empty() {
62 write!(f, "Multi-character shorts: \n{:#?}", self.multi_char_shorts)?;
63 }
64 if self.has_empty_switch {
65 write!(f, "Argument specified with neither short nor long switch")?;
66 }
67 Ok(())
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Hash)]
72struct SwitchInfo {
73 common: SwitchCommon,
74 arity: SwitchShape,
75}
76
77#[derive(Default, Debug)]
78struct SwitchTable {
79 table: HashMap<String, HashSet<SwitchInfo>>,
80}
81
82impl SwitchTable {
83 fn insert(&mut self, key: &str, info: SwitchInfo) {
84 self.table
85 .entry(key.to_string())
86 .or_insert_with(HashSet::default)
87 .insert(info);
88 }
89 fn keys_with_multiple_values(&self) -> impl Iterator<Item = String> + '_ {
90 self.table.iter().filter_map(
91 |(k, v)| {
92 if v.len() > 1 {
93 Some(k.clone())
94 } else {
95 None
96 }
97 },
98 )
99 }
100}
101
102#[derive(Default, Debug)]
103struct InvalidNames {
104 one_char_longs: Vec<String>,
105 multi_char_shorts: Vec<String>,
106 has_empty_switch: bool,
107}
108
109impl InvalidNames {
110 fn insert(&mut self, info: &SwitchInfo) {
111 let short = info.common.short.as_str();
112 let long = info.common.long.as_str();
113 if short.len() > 1 {
114 self.multi_char_shorts.push(short.to_string());
115 }
116 if long.len() == 1 {
117 self.one_char_longs.push(long.to_string());
118 }
119 if short.len() == 0 && long.len() == 0 {
120 self.has_empty_switch = true;
121 }
122 }
123}
124
125#[derive(Default, Debug)]
126struct Duplicates {
127 by_short: SwitchTable,
128 by_long: SwitchTable,
129}
130
131impl Duplicates {
132 fn insert(&mut self, info: SwitchInfo) {
133 let short = info.common.short.clone();
134 let long = info.common.long.clone();
135 if short.len() > 0 {
136 self.by_short.insert(&short, info.clone());
137 }
138 if long.len() > 0 {
139 self.by_long.insert(&long, info);
140 }
141 }
142}
143
144#[derive(Default, Debug)]
145pub struct Checker {
146 duplicates: Duplicates,
147 invalid_names: InvalidNames,
148}
149
150impl Checker {
151 fn insert(&mut self, info: SwitchInfo) {
152 self.invalid_names.insert(&info);
153 self.duplicates.insert(info);
154 }
155 pub fn invalid(self) -> Option<Invalid> {
156 let Checker {
157 duplicates: Duplicates { by_short, by_long },
158 invalid_names:
159 InvalidNames {
160 one_char_longs,
161 multi_char_shorts,
162 has_empty_switch,
163 },
164 } = self;
165 let duplicate_shorts = by_short.keys_with_multiple_values().collect::<Vec<_>>();
166 let duplicate_longs = by_long.keys_with_multiple_values().collect::<Vec<_>>();
167 Invalid::new(
168 duplicate_shorts,
169 duplicate_longs,
170 one_char_longs,
171 multi_char_shorts,
172 has_empty_switch,
173 )
174 }
175}
176
177impl Switches for Checker {
178 fn add(&mut self, common: SwitchCommon, arity: SwitchShape) {
179 self.insert(SwitchInfo { common, arity });
180 }
181}