1use crate::{pascal_case, Result, Settings};
2use std::{borrow::Cow, collections::HashMap};
3
4#[derive(Debug)]
6pub struct Provider {
7 args: Vec<String>,
8 switch_mappings: HashMap<String, String>,
9}
10
11impl Provider {
12 pub fn new<I, V, S>(args: I, switch_mappings: &[(S, S)]) -> Self
24 where
25 I: Iterator<Item = V>,
26 V: AsRef<str>,
27 S: AsRef<str>,
28 {
29 Self {
30 args: args.map(|a| a.as_ref().to_owned()).collect(),
31 switch_mappings: switch_mappings
32 .iter()
33 .filter(|m| m.0.as_ref().starts_with("--") || m.0.as_ref().starts_with('-'))
34 .map(|(k, v)| (k.as_ref().to_uppercase(), v.as_ref().to_owned()))
35 .collect(),
36 }
37 }
38}
39
40impl crate::Provider for Provider {
41 #[inline]
42 fn name(&self) -> &str {
43 "Command Line"
44 }
45
46 fn load(&self, settings: &mut Settings) -> Result {
47 let mut args = self.args.iter();
48
49 while let Some(arg) = args.next() {
50 let mut current = Cow::Borrowed(arg);
51 let start: usize = if arg.starts_with("--") {
52 2
53 } else if arg.starts_with('-') {
54 1
55 } else if arg.starts_with('/') {
56 let mut temp = arg.clone();
58 temp.replace_range(0..1, "--");
59 current = Cow::Owned(temp);
60 2
61 } else {
62 0
63 };
64
65 let key;
66 let value;
67
68 if let Some(separator) = current.find('=') {
69 let segment: String = current
70 .chars()
71 .take(separator)
72 .map(|c| c.to_ascii_uppercase())
73 .collect();
74
75 key = if let Some(mapping) = self.switch_mappings.get(&segment) {
76 mapping.clone()
77 } else if start == 1 {
78 continue;
79 } else {
80 current.chars().skip(start).take(separator - start).collect()
81 };
82
83 value = current.chars().skip(separator + 1).collect();
84 } else {
85 if start == 0 {
86 continue;
87 }
88
89 key = if let Some(mapping) = self.switch_mappings.get(¤t.to_uppercase()) {
90 mapping.clone()
91 } else if start == 1 {
92 continue;
93 } else {
94 current.chars().skip(start).collect()
95 };
96
97 let Some(next) = args.next() else {
98 continue;
99 };
100
101 value = next.clone();
102 }
103
104 settings.insert(pascal_case(&key), value);
105 }
106
107 Ok(())
108 }
109}
110
111impl<I, V> From<I> for Provider
112where
113 I: Iterator<Item = V>,
114 V: AsRef<str>,
115{
116 #[inline]
117 fn from(value: I) -> Self {
118 Self::new(value, &Vec::<(&str, &str)>::new())
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use crate::Provider as _;
126
127 #[test]
128 fn load_should_ignore_unknown_arguments() {
129 let args = ["foo", "/bar=baz"].iter();
131 let provider = Provider::from(args);
132 let mut settings = Settings::default();
133
134 provider.load(&mut settings).unwrap();
136
137 println!("{settings:?}");
138
139 assert_eq!(settings.len(), 1);
141 assert_eq!(settings.get("bar"), Some("baz"));
142 }
143
144 #[test]
145 fn load_should_ignore_arguments_in_the_middle() {
146 let args = [
148 "Key1=Value1",
149 "--Key2=Value2",
150 "/Key3=Value3",
151 "Bogus1",
152 "--Key4",
153 "Value4",
154 "Bogus2",
155 "/Key5",
156 "Value5",
157 "Bogus3",
158 ]
159 .iter();
160 let provider = Provider::from(args);
161 let mut settings = Settings::default();
162
163 provider.load(&mut settings).unwrap();
165
166 assert_eq!(settings.get("Key1"), Some("Value1"));
168 assert_eq!(settings.get("Key2"), Some("Value2"));
169 assert_eq!(settings.get("Key3"), Some("Value3"));
170 assert_eq!(settings.get("Key4"), Some("Value4"));
171 assert_eq!(settings.get("Key5"), Some("Value5"));
172 }
173
174 #[test]
175 fn load_should_process_key_value_pairs_without_mappings() {
176 let args = [
178 "Key1=Value1",
179 "--Key2=Value2",
180 "/Key3=Value3",
181 "--Key4",
182 "Value4",
183 "/Key5",
184 "Value5",
185 "--single=1",
186 "--two-part=2",
187 ]
188 .iter();
189 let provider = Provider::from(args);
190 let mut settings = Settings::default();
191
192 provider.load(&mut settings).unwrap();
194
195 assert_eq!(settings.get("Key1"), Some("Value1"));
197 assert_eq!(settings.get("Key2"), Some("Value2"));
198 assert_eq!(settings.get("Key3"), Some("Value3"));
199 assert_eq!(settings.get("Key4"), Some("Value4"));
200 assert_eq!(settings.get("Key5"), Some("Value5"));
201 assert_eq!(settings.get("Single"), Some("1"));
202 assert_eq!(settings.get("TwoPart"), Some("2"));
203 }
204
205 #[test]
206 fn load_should_process_key_value_pairs_with_mappings() {
207 let args = [
209 "-K1=Value1",
210 "--Key2=Value2",
211 "/Key3=Value3",
212 "--Key4",
213 "Value4",
214 "/Key5",
215 "Value5",
216 "/Key6=Value6",
217 ]
218 .iter();
219 let switch_mappings = [
220 ("-K1", "LongKey1"),
221 ("--Key2", "SuperLongKey2"),
222 ("--Key6", "SuchALongKey6"),
223 ];
224 let provider = Provider::new(args, &switch_mappings);
225 let mut settings = Settings::default();
226
227 provider.load(&mut settings).unwrap();
229
230 assert_eq!(settings.get("LongKey1"), Some("Value1"));
232 assert_eq!(settings.get("SuperLongKey2"), Some("Value2"));
233 assert_eq!(settings.get("Key3"), Some("Value3"));
234 assert_eq!(settings.get("Key4"), Some("Value4"));
235 assert_eq!(settings.get("Key5"), Some("Value5"));
236 assert_eq!(settings.get("SuchALongKey6"), Some("Value6"));
237 }
238
239 #[test]
240 fn load_should_override_value_when_key_is_duplicated() {
241 let args = ["/Key1=Value1", "--Key1=Value2"].iter();
243 let provider = Provider::from(args);
244 let mut settings = Settings::default();
245
246 provider.load(&mut settings).unwrap();
248
249 assert_eq!(settings.get("Key1"), Some("Value2"));
251 }
252
253 #[test]
254 fn load_should_ignore_key_when_value_is_missing() {
255 let args = ["--Key1", "Value1", "/Key2"].iter();
257 let provider = Provider::from(args);
258 let mut settings = Settings::default();
259
260 provider.load(&mut settings).unwrap();
262
263 assert_eq!(settings.len(), 1);
265 assert_eq!(settings.get("Key1"), Some("Value1"));
266 }
267
268 #[test]
269 fn load_should_ignore_unrecognizable_argument() {
270 let args = ["ArgWithoutPrefixAndEqualSign"].iter();
272 let provider = Provider::from(args);
273 let mut settings = Settings::default();
274
275 provider.load(&mut settings).unwrap();
277
278 assert!(settings.is_empty());
280 }
281
282 #[test]
283 fn load_should_ignore_argument_when_short_switch_is_undefined() {
284 let args = ["-Key1", "Value1"].iter();
286 let switch_mappings = [("-Key2", "LongKey2")];
287 let provider = Provider::new(args, &switch_mappings);
288 let mut settings = Settings::default();
289
290 provider.load(&mut settings).unwrap();
292
293 assert!(settings.is_empty());
295 }
296}