rexl/cli/
arg_parse.rs

1use std::collections::HashMap;
2use std::fmt::Debug;
3use std::hash::Hash;
4
5use crate::cli::{Argument, ArgumentKind};
6
7/// I will not give the implement about CLI i18n since I believe less is more
8#[derive(Debug)]
9pub struct ArgParser<K: Hash + Eq + Debug + Clone> {
10    /// multi argparse-names to one key
11    pub(crate) names_key_map:    HashMap<String, K>,
12    /// key, argument
13    pub(crate) key_argument_map: HashMap<K, Argument<K>>,
14    /// key, value
15    pub(crate) string_map:       HashMap<K, String>,
16    pub(crate) bool_map:         HashMap<K, bool>,
17    pub(crate) integer_map:      HashMap<K, i64>,
18    pub(crate) float_map:        HashMap<K, f64>,
19    /// multiple values
20    pub(crate) strings_map:      HashMap<K, Vec<String>>,
21    pub(crate) integers_map:     HashMap<K, Vec<i64>>,
22    pub(crate) floats_map:       HashMap<K, Vec<f64>>,
23    pub(crate) properties_map:   HashMap<K, HashMap<String, String>>,
24    /// extra values
25    pub(crate) extra_values:     Vec<String>,
26}
27
28impl<K> ArgParser<K>
29where K: Hash + Eq + Debug + Clone
30{
31    pub fn new() -> Self {
32        Self {
33            names_key_map:    HashMap::new(),
34            key_argument_map: HashMap::new(),
35
36            string_map:  HashMap::new(),
37            bool_map:    HashMap::new(),
38            integer_map: HashMap::new(),
39            float_map:   HashMap::new(),
40
41            strings_map:  HashMap::new(),
42            integers_map: HashMap::new(),
43            floats_map:   HashMap::new(),
44
45            properties_map: HashMap::new(),
46            extra_values:   Vec::new(),
47        }
48    }
49
50    #[inline]
51    pub fn get_bool<T>(&self, key: T) -> bool
52    where T: Into<K> {
53        if let Some(v) = self.bool_map.get(&key.into()) {
54            *v
55        } else {
56            false
57        }
58    }
59
60    #[inline]
61    pub fn get_string<T>(&self, key: T) -> Option<&String>
62    where T: Into<K> {
63        self.string_map.get(&key.into())
64    }
65
66    #[inline]
67    pub fn get_string_or_else<T, F>(&self, key: T, f: F) -> String
68    where
69        T: Into<K>,
70        F: FnOnce() -> String, {
71        if let Some(v) = self.get_string(key) {
72            v.clone()
73        } else {
74            f()
75        }
76    }
77
78    #[inline]
79    pub fn get_integer<T>(&self, key: T) -> Option<&i64>
80    where T: Into<K> {
81        self.integer_map.get(&key.into())
82    }
83
84    #[inline]
85    pub fn get_integer_or_else<T, F>(&self, key: T, f: F) -> i64
86    where
87        T: Into<K>,
88        F: FnOnce() -> i64, {
89        if let Some(v) = self.get_integer(key) {
90            *v
91        } else {
92            f()
93        }
94    }
95
96    #[inline]
97    pub fn get_float<T>(&self, key: T) -> Option<&f64>
98    where T: Into<K> {
99        self.float_map.get(&key.into())
100    }
101
102    #[inline]
103    pub fn get_float_or_else<T, F>(&self, key: T, f: F) -> f64
104    where
105        T: Into<K>,
106        F: FnOnce() -> f64, {
107        if let Some(v) = self.get_float(key) {
108            *v
109        } else {
110            f()
111        }
112    }
113
114    #[inline]
115    pub fn get_strings<T>(&self, key: T) -> Option<&Vec<String>>
116    where T: Into<K> {
117        self.strings_map.get(&key.into())
118    }
119
120    #[inline]
121    pub fn get_strings_or_else<T, F>(&self, key: T, f: F) -> Vec<String>
122    where
123        T: Into<K>,
124        F: FnOnce() -> Vec<String>, {
125        if let Some(v) = self.get_strings(key) {
126            v.clone()
127        } else {
128            f()
129        }
130    }
131
132    #[inline]
133    pub fn get_strings_or_empty<T>(&self, key: T) -> Vec<String>
134    where T: Into<K> {
135        self.get_strings_or_else(
136            key,
137            #[inline]
138            || Vec::new(),
139        )
140    }
141
142    #[inline]
143    pub fn get_integers<T>(&self, key: T) -> Option<&Vec<i64>>
144    where T: Into<K> {
145        self.integers_map.get(&key.into())
146    }
147
148    #[inline]
149    pub fn get_integers_or_else<T, F>(&self, key: T, f: F) -> Vec<i64>
150    where
151        T: Into<K>,
152        F: FnOnce() -> Vec<i64>, {
153        if let Some(v) = self.get_integers(key) {
154            v.clone()
155        } else {
156            f()
157        }
158    }
159
160    #[inline]
161    pub fn get_integers_or_empty<T>(&self, key: T) -> Vec<i64>
162    where T: Into<K> {
163        self.get_integers_or_else(
164            key,
165            #[inline]
166            || Vec::new(),
167        )
168    }
169
170    #[inline]
171    pub fn get_floats<T>(&self, key: T) -> Option<&Vec<f64>>
172    where T: Into<K> {
173        self.floats_map.get(&key.into())
174    }
175
176    #[inline]
177    pub fn get_floats_or_else<T, F>(&self, key: T, f: F) -> Vec<f64>
178    where
179        T: Into<K>,
180        F: FnOnce() -> Vec<f64>, {
181        if let Some(v) = self.get_floats(key) {
182            v.clone()
183        } else {
184            f()
185        }
186    }
187
188    #[inline]
189    pub fn get_floats_or_empty<T>(&self, key: T) -> Vec<f64>
190    where T: Into<K> {
191        self.get_floats_or_else(
192            key,
193            #[inline]
194            || Vec::new(),
195        )
196    }
197
198    #[inline]
199    pub fn get_properties<T: Into<K>>(&self, key: T) -> Option<&HashMap<String, String>> {
200        self.properties_map.get(&key.into())
201    }
202
203    #[inline]
204    pub fn get_properties_or_else<T, F>(&self, key: T, f: F) -> HashMap<String, String>
205    where
206        T: Into<K>,
207        F: FnOnce() -> HashMap<String, String>, {
208        if let Some(v) = self.get_properties(key) {
209            v.clone()
210        } else {
211            f()
212        }
213    }
214
215    #[inline]
216    pub fn get_properties_or_empty<T>(&self, key: T) -> HashMap<String, String>
217    where T: Into<K> {
218        self.get_properties_or_else(
219            key,
220            #[inline]
221            || HashMap::new(),
222        )
223    }
224
225    #[inline]
226    pub fn get_extra_values(&self) -> &Vec<String> {
227        &self.extra_values
228    }
229
230    // -a 1.ts -a 2.ts
231    #[inline]
232    pub fn add<T: Into<K>>(&mut self, key: T, names: Vec<&str>) -> &mut Self {
233        return self.add_by_kind(key, names, ArgumentKind::String, false)
234    }
235
236    #[inline]
237    pub fn add_multiple<T: Into<K>>(&mut self, key: T, names: Vec<&str>) -> &mut Self {
238        return self.add_by_kind(key, names, ArgumentKind::String, true)
239    }
240
241    #[inline]
242    pub fn add_integer<T: Into<K>>(&mut self, key: T, names: Vec<&str>) -> &mut Self {
243        return self.add_by_kind(key, names, ArgumentKind::Integer, false)
244    }
245
246    #[inline]
247    pub fn add_integer_multiple<T: Into<K>>(&mut self, key: T, names: Vec<&str>) -> &mut Self {
248        return self.add_by_kind(key, names, ArgumentKind::Integer, true)
249    }
250
251    #[inline]
252    pub fn add_float<T: Into<K>>(&mut self, key: T, names: Vec<&str>) -> &mut Self {
253        return self.add_by_kind(key, names, ArgumentKind::Float, false)
254    }
255
256    #[inline]
257    pub fn add_float_multiple<T: Into<K>>(&mut self, key: T, names: Vec<&str>) -> &mut Self {
258        return self.add_by_kind(key, names, ArgumentKind::Float, true)
259    }
260
261    /// aux, lah, cvzf, -ef
262    /// only allow single char key and bool value
263    #[inline]
264    pub fn add_bool<T: Into<K>>(&mut self, key: T, names: Vec<&str>) -> &mut Self {
265        return self.add_by_kind(key, names, ArgumentKind::Bool, false)
266    }
267
268    /// only support single char argparse
269    /// -Dfile.encoding=utf8 -Dspring.active.profile=dev
270    #[inline]
271    pub fn add_property<T: Into<K>>(&mut self, key: T, names: Vec<&str>) -> &mut Self {
272        return self.add_by_kind(key, names, ArgumentKind::Property, true)
273    }
274
275    /// -L 3, -L=3, -L3, --level 3, --level=3
276    /// -o yaml, -o=yaml, -oyaml, --output yaml, --output=yaml
277    /// -r, -r=true, -r true, --rm, --rm=true, --rm true
278    pub fn add_by_kind<T>(
279        &mut self, key: T, names: Vec<&str>, kind: ArgumentKind, multiple: bool,
280    ) -> &mut Self
281    where T: Into<K> {
282        let key = key.into();
283        let names = names.iter().map(|s| s.to_string()).collect();
284        self.key_argument_map.insert(key.clone(), Argument {
285            key,
286            names,
287            kind,
288            multiple,
289        });
290        return self
291    }
292}