simon/
validation.rs

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}