1pub mod argument;
2
3use std::{borrow::BorrowMut, env, iter::Peekable};
4
5use argument::{legacy_argument::Argument, parsable_argument::HandleableArgument};
6
7pub struct ArgumentList<'a> {
19 pub dangling_values: Vec<String>,
20 pub arguments: Vec<Argument>,
21 pub parsable_arguments: Vec<&'a mut (dyn HandleableArgument<'a> + 'a)>,
22}
23
24impl<'a> ArgumentList<'a> {
25 pub fn arguments(&self) -> &Vec<Argument> {
26 &self.arguments
27 }
28 pub fn new() -> ArgumentList<'a> {
32 ArgumentList {
33 dangling_values: Vec::new(),
34 arguments: Vec::new(),
35 parsable_arguments: Vec::new(),
36 }
37 }
38
39 pub fn append_arg(&mut self, argument: Argument) {
43 self.arguments.push(argument);
44 }
45
46 pub fn append_dangling_value(&mut self, value: &str) {
50 self.dangling_values.push(String::from(value));
51 }
52
53 pub fn search_by_short_name(&self, name: char) -> Option<&Argument> {
57 for x in &self.arguments {
58 match x.short() {
59 Some(symbol) => {
60 if symbol == &name {
61 return Some(x);
62 }
63 }
64 None => (),
65 };
66 }
67 Option::None
68 }
69
70 pub fn search_by_short_name_mut(&mut self, name: char) -> Option<&mut Argument> {
74 for x in &mut self.arguments {
75 match x.short() {
76 Some(symbol) => {
77 if symbol == &name {
78 return Some(x);
79 }
80 }
81 None => (),
82 };
83 }
84 Option::None
85 }
86
87 fn handle_parsable_short_name(
88 &mut self,
89 name: char,
90 input_iter: &mut Peekable<&mut std::slice::Iter<'_, String>>,
91 ) -> Result<bool, String> {
92 for x in &mut self.parsable_arguments {
93 if x.is_by_short(name) {
94 x.handle(input_iter)?;
95 return Result::Ok(true);
96 }
97 }
98 return Result::Ok(false);
99 }
100
101 fn handle_parsable_long_name(
102 &mut self,
103 name: &str,
104 input_iter: &mut Peekable<&mut std::slice::Iter<'_, String>>,
105 ) -> Result<bool, String> {
106 for x in &mut self.parsable_arguments {
107 if x.is_by_long(name) {
108 x.handle(input_iter)?;
109 return Result::Ok(true);
110 }
111 }
112 return Result::Ok(false);
113 }
114
115 pub fn search_by_long_name(&self, name: &str) -> Option<&Argument> {
116 for x in &self.arguments {
117 match x.long() {
118 Some(ref long_name) => {
119 if long_name == name {
120 return Option::Some(x);
121 }
122 }
123 None => (),
124 }
125 }
126 Option::None
127 }
128
129 pub fn search_by_long_name_mut(&mut self, name: &str) -> Option<&mut Argument> {
133 for x in &mut self.arguments {
134 match x.long() {
135 Some(ref long_name) => {
136 if long_name == name {
137 return Option::Some(x);
138 }
139 }
140 None => (),
141 }
142 }
143 Option::None
144 }
145
146 pub fn get_dangling_values(&self) -> &Vec<String> {
148 &self.dangling_values
149 }
150
151 pub fn parse_args(&mut self, input: Vec<String>) -> Result<(), String> {
177 let mut iter = input.iter();
178 let mut input_iter = iter.borrow_mut().peekable();
179 while let Some(word) = input_iter.next() {
180 let word_length = word.chars().count();
182 if word_length == 2 {
183 if word.chars().nth(0).expect("first letter") == '-'
184 && word
185 .chars()
186 .nth(1)
187 .expect(&format!("{}", word_length))
188 .is_alphabetic()
189 {
190 match self.search_by_short_name_mut(word.chars().nth(1).unwrap()) {
192 Some(argument) => {
193 argument.add_value(&mut input_iter)?;
194 }
195 None => {
196 if !self.handle_parsable_short_name(
197 word.chars().nth(1).unwrap(),
198 &mut input_iter,
199 )? {
200 return Err(format!(
201 "Could not find argument identified by {}.",
202 word
203 ));
204 }
205 }
206 };
207 } else {
208 self.append_dangling_value(word);
210 }
211 } else if word_length > 2 {
212 if word.chars().nth(0).unwrap() == '-'
213 && word.chars().nth(1).unwrap() == '-'
214 && word.chars().nth(2).unwrap().is_alphabetic()
215 {
216 match self.search_by_long_name_mut(&word[2..word.len()]) {
218 Some(argument) => {
219 argument.add_value(&mut input_iter)?;
220 }
221 Option::None => {
222 if !self
223 .handle_parsable_long_name(&word[2..word.len()], &mut input_iter)?
224 {
225 return Err(format!(
226 "Could not find argument identified by {}.",
227 word
228 ));
229 }
230 }
231 };
232 } else {
233 self.append_dangling_value(word);
235 }
236 } else {
237 self.append_dangling_value(word);
239 }
240 }
241
242 Ok(())
244 }
245
246 pub fn register_parsable(&mut self, arg: &'a mut impl HandleableArgument<'a>) {
250 self.parsable_arguments.push(arg);
251 }
252}
253
254pub fn args_to_string_vector(args: env::Args) -> Vec<String> {
258 let mut arguments = Vec::new();
259
260 for x in args {
261 arguments.push(String::from(x));
262 }
263 arguments
264}
265
266#[cfg(test)]
267mod tests {
268 use crate::argument::{
269 legacy_argument::{ArgResult, ArgType},
270 parsable_argument::ParsableValueArgument,
271 };
272
273 use super::{argument::ArgumentIdentification, *};
274
275 #[test]
276 fn parse_works() {
277 let args = vec![
278 String::from("-d"),
279 String::from("-p"),
280 String::from("/file"),
281 String::from("--an-list"),
282 String::from("Marcin"),
283 String::from("-l"),
284 String::from("Mazgaj"),
285 ];
286
287 let mut args_list = ArgumentList::new();
288 args_list.append_arg(Argument::new(Some('d'), None, ArgType::Flag).expect("append 1"));
289 args_list.append_arg(Argument::new(Some('p'), None, ArgType::Value).expect("append 2"));
290 args_list.append_arg(
291 Argument::new(Some('l'), Some("an-list"), ArgType::ValueList).expect("append 3"),
292 );
293 args_list.parse_args(args).unwrap();
294 assert_eq!(args_list.arguments()[0].arg_result, Some(ArgResult::Flag));
295 assert_eq!(
296 args_list.arguments[1].arg_result,
297 Some(ArgResult::Value(String::from("/file")))
298 );
299 assert_eq!(
300 args_list.arguments[2].arg_result,
301 Some(ArgResult::ValueList(vec![
302 String::from("Marcin"),
303 String::from("Mazgaj")
304 ]))
305 );
306
307 assert_eq!(
308 args_list
309 .search_by_short_name('d')
310 .unwrap()
311 .get_flag()
312 .unwrap(),
313 true
314 );
315 assert_eq!(
316 args_list
317 .search_by_long_name("an-list")
318 .unwrap()
319 .get_values()
320 .unwrap(),
321 &vec![String::from("Marcin"), String::from("Mazgaj")]
322 );
323 }
324
325 #[test]
326 fn get_dangling_values_works() {
327 let args = vec![
328 String::from("-d"),
329 String::from("-p"),
330 String::from("/file"),
331 String::from("--an-list"),
332 String::from("Marcin"),
333 String::from("-l"),
334 String::from("Mazgaj"),
335 String::from("dangling"),
336 ];
337
338 let mut args_list = ArgumentList::new();
339
340 args_list.append_arg(Argument::new(Some('d'), None, ArgType::Flag).expect("append 1"));
341 args_list.append_arg(Argument::new(Some('p'), None, ArgType::Value).expect("append 2"));
342 args_list.append_arg(
343 Argument::new(Some('l'), Some("an-list"), ArgType::ValueList).expect("append 3"),
344 );
345
346 args_list.parse_args(args).unwrap();
347
348 let dangling = args_list.get_dangling_values();
349
350 assert_eq!("dangling", dangling[0]);
351 }
352
353 #[test]
354 fn values_with_spaces_work() {
355 let args = vec![
356 String::from("-n"),
357 String::from("Marcin Mazgaj"),
358 String::from("--hello"),
359 String::from("Hello World!"),
360 String::from("--hello"),
361 String::from("Witaj Świecie!"),
362 ];
363
364 let mut args_list = ArgumentList::new();
365
366 args_list.append_arg(Argument::new(Some('n'), None, ArgType::Value).unwrap());
367 args_list.append_arg(Argument::new(None, Some("hello"), ArgType::ValueList).unwrap());
368
369 args_list.parse_args(args).unwrap();
370
371 assert_eq!(
372 args_list
373 .search_by_short_name('n')
374 .unwrap()
375 .get_value()
376 .unwrap(),
377 "Marcin Mazgaj"
378 );
379 assert_eq!(
380 args_list
381 .search_by_long_name("hello")
382 .unwrap()
383 .get_values()
384 .unwrap(),
385 &vec![String::from("Hello World!"), String::from("Witaj Świecie!")]
386 );
387 }
388
389 #[test]
390 fn parse_with_parsable_arguments_works() {
391 let args = vec![
392 String::from("-n"),
393 String::from("5"),
394 String::from("--hello"),
395 String::from("Hello World!"),
396 String::from("--hello"),
397 String::from("Witaj Świecie!"),
398 ];
399
400 let mut args_list = ArgumentList::new();
401 let mut argument_int =
402 ParsableValueArgument::new_integer(ArgumentIdentification::Short('n'));
403 let mut argument_str =
404 ParsableValueArgument::new_string(ArgumentIdentification::Long(String::from("hello")));
405 args_list.register_parsable(&mut argument_int);
406 args_list.register_parsable(&mut argument_str);
407 args_list
408 .parse_args(args)
409 .expect("Failed while parsing arguments");
410 assert_eq!(argument_int.first_value().unwrap(), &5);
411 assert_eq!(argument_str.first_value().unwrap(), "Hello World!");
412 assert_eq!(argument_str.values().get(1).unwrap(), "Witaj Świecie!");
413 }
414
415 #[test]
416 fn parse_drop_parser_works() {
417 let args = vec![
418 String::from("-n"),
419 String::from("5"),
420 String::from("--hello"),
421 String::from("Hello World!"),
422 String::from("--hello"),
423 String::from("Witaj Świecie!"),
424 ];
425 let mut argument_int =
426 ParsableValueArgument::new_integer(ArgumentIdentification::Short('n'));
427 let mut argument_str =
428 ParsableValueArgument::new_string(ArgumentIdentification::Long(String::from("hello")));
429 {
430 let mut args_list = ArgumentList::new();
431 args_list.register_parsable(&mut argument_int);
432 args_list.register_parsable(&mut argument_str);
433 args_list
434 .parse_args(args)
435 .expect("Failed while parsing arguments");
436 }
437 assert_eq!(argument_int.first_value().unwrap(), &5);
438 assert_eq!(argument_str.first_value().unwrap(), "Hello World!");
439 assert_eq!(argument_str.values().get(1).unwrap(), "Witaj Świecie!");
440 }
441
442 #[test]
443 fn parse_with_mixed_arguments_works() {
444 let args = vec![
445 String::from("-n"),
446 String::from("5"),
447 String::from("--hello"),
448 String::from("Hello World!"),
449 String::from("--hello"),
450 String::from("Witaj Świecie!"),
451 ];
452
453 let mut args_list = ArgumentList::new();
454 let mut argument_str =
455 ParsableValueArgument::new_string(ArgumentIdentification::Long(String::from("hello")));
456 args_list.register_parsable(&mut argument_str);
457 args_list.append_arg(Argument::new(Some('n'), None, ArgType::Value).unwrap());
458 args_list
459 .parse_args(args)
460 .expect("Failed while parsing arguments");
461 assert_eq!(
462 args_list
463 .search_by_short_name('n')
464 .unwrap()
465 .get_value()
466 .unwrap(),
467 "5"
468 );
469 assert_eq!(argument_str.first_value().unwrap(), "Hello World!");
470 assert_eq!(argument_str.values().get(1).unwrap(), "Witaj Świecie!");
471 }
472}