1use std::fmt::Debug;
148use std::fmt::Formatter;
149use std::slice::Iter;
150
151use error::Error;
152
153pub enum Opt<'a> {
155 Switch(&'a str, &'a mut bool),
157 Arg(&'a str, &'a mut String),
159 SubSwitch(&'a str, FnSubSwitch<'a>),
161 SubArg(&'a str, FnSubArg<'a>),
163 Free(FnFree<'a>),
165}
166
167pub mod error {
168 use std::fmt::Display;
169 use std::fmt::Formatter;
170
171 #[derive(Clone,Debug,Eq,PartialEq)]
173 pub enum Error {
174 NeedArgument(String),
177 Unexpected(String),
180 }
181
182
183 impl Display for Error {
184 fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
185 match self {
186 Error::NeedArgument(s) => write!(f, "need argument for '{}'", s),
187 Error::Unexpected(s) => write!(f, "unexpected argument '{}'", s),
188 }
189 }
190 }
191}
192
193pub type FnSubSwitch<'a> = &'a dyn Fn(&str);
216pub type FnSubArg<'a> = &'a dyn Fn(&str,&str);
239pub type FnFree<'a> = &'a dyn Fn(&str);
262
263impl Debug for Opt<'_> {
264 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
265 match self {
266 Opt::Switch(x, b) => f.debug_tuple("Opt::Switch").field(x).field(b).finish(),
267 Opt::Arg(x, s) => f.debug_tuple("Opt::Arg").field(x).field(s).finish(),
268 Opt::SubArg(x, _) => f.debug_tuple("Opt::SubArg").field(x).field(&"Fn").finish(),
269 Opt::SubSwitch(x, _) => f.debug_tuple("Opt::SubSwitch").field(x).field(&"Fn").finish(),
270 Opt::Free(_) => f.debug_tuple("Opt::Free").field(&"Fn").finish(),
271 }
272 }
273}
274
275fn split_off(slice: &str) -> Result<(&str, &str),&str> {
277 let split = slice.split_once('|');
278 if let Some((s1, s2)) = split {
279 Ok((s1, s2))
280 }
281 else {
282 Err(slice)
283 }
284}
285
286fn match_short(ch: char, switch: &str) -> bool {
288 let mut buf = [0u8; 4];
289 let ch: &str = ch.encode_utf8(&mut buf);
290 let split = split_off(switch);
291 if let Ok((s1, s2)) = split {
292 if s1 == ch {
293 return true;
294 }
295 else if s2 == ch {
296 return true;
297 }
298 }
299 else if let Err(s) = split {
300 if s == ch {
301 return true;
302 }
303 }
304 return false;
305}
306
307fn match_long(arg: &str, switch: &str) -> bool {
309 if arg.chars().count() == 1 {
310 return false;
311 }
312
313 let split = split_off(switch);
314 if let Ok((s1, s2)) = split {
315 if s1 == arg {
316 return true;
317 }
318 else if s2 == arg {
319 return true;
320 }
321 }
322 else if let Err(s) = split {
323 if s == arg {
324 return true;
325 }
326 }
327 return false;
328}
329
330fn handle_short_opt(args_iter: &mut Iter<&str>, arg: &str, opts: &mut [Opt]) -> Result<bool,Error> {
334 let mut chars_iter = arg.chars().peekable();
335 let mut parsed = false;
336 while let Some(ch) = chars_iter.next() {
337 let mut iter = opts.iter_mut();
338 while let Some(opt) = iter.next() {
339 if let Opt::Switch(switch, &mut ref mut b) = opt {
340 if match_short(ch, switch) {
341 *b = true;
342 parsed = true;
343 }
344 }
345 else if let Opt::Arg(switch, &mut ref mut s) = opt {
346 if match_short(ch, switch) {
347 if chars_iter.peek().is_some() {
348 let a: String = chars_iter.collect();
349 *s = a;
350 return Ok(true);
353 }
354 else if let Some(a) = args_iter.next() {
355 *s = a.to_string();
356 parsed = true;
357 }
358 else {
359 return Err(Error::NeedArgument(format!("-{}", arg)));
360 }
361 }
362 }
363 else if let Opt::SubArg(switch, func) = opt {
364 if match_short(ch, switch) {
365 if let Some(a) = args_iter.next() {
366 func(arg, *a);
367 parsed = true;
368 }
369 else {
370 return Err(Error::NeedArgument(format!("-{}", arg)));
371 }
372 }
373 }
374 else if let Opt::SubSwitch(switch, func) = opt {
375 if match_short(ch, switch) {
376 func(arg);
377 parsed = true;
378 }
379 }
380 }
381 }
382
383 if parsed {
384 return Ok(true);
385 }
386 Err(Error::Unexpected(format!("-{}", arg)))
387}
388
389fn extract_equals(arg: &str) -> Option<(&str,&str)> {
391 let split = arg.split_once('=');
392 if let Some((arg, equals_value)) = split {
393 Some((arg, equals_value))
394 }
395 else {
396 None
397 }
398}
399
400fn handle_long_opt(args_iter: &mut Iter<&str>, arg: &str, opts: &mut [Opt]) -> Result<bool,Error> {
402 for opt in &mut *opts {
403 if let Opt::Switch(switch, &mut ref mut b) = opt {
404 if switch.chars().count() == 1 {
405 continue;
406 }
407
408 if match_long(arg, switch) {
409 *b = true;
410 return Ok(true);
411 }
412 }
413 else if let Opt::Arg(switch, &mut ref mut s) = opt {
414 let (arg, equals_value) = extract_equals(arg).unwrap_or((arg, ""));
415
416 if match_long(arg, switch) {
417 if equals_value != "" {
418 *s = (equals_value).to_string();
419 return Ok(true);
420 }
421 else if let Some(a) = args_iter.next() {
422 *s = (*a).to_string();
423 return Ok(true);
424 }
425 else {
426 return Err(Error::NeedArgument(format!("--{}", arg)));
427 }
428 }
429 }
430 else if let Opt::SubArg(switch, func) = opt {
431 let (arg, equals_value) = extract_equals(arg).unwrap_or((arg, ""));
432 if match_long(arg, switch) {
433 if equals_value != "" {
434 func(arg, equals_value);
435 return Ok(true);
436 }
437 else if let Some(a) = args_iter.next() {
438 func(arg, *a);
439 return Ok(true);
440 }
441 else {
442 return Err(Error::NeedArgument(format!("--{}", arg)));
443 }
444 }
445 }
446 else if let Opt::SubSwitch(switch, func) = opt {
447 if match_long(arg, switch) {
448 func(arg);
449 return Ok(true);
450 }
451 }
452 }
453 Err(Error::Unexpected(format!("--{}", arg)))
454}
455
456fn handle_free_arg(arg: &str, opts: &mut [Opt]) -> Result<(),Error> {
460 for opt in opts.iter_mut() {
461 if let Opt::Free(f) = opt {
462 let f: FnFree = f;
463 f(arg);
464 return Ok(());
465 }
466 }
467 if arg == "--" {
468 return Ok(());
469 }
470 Err(Error::Unexpected(arg.to_string()))
471}
472
473pub fn get_options_env(opts: &mut [Opt]) -> Result<(),Error> {
475 let env_args: Vec<String> = std::env::args().collect();
476 get_options(opts, &env_args)
477}
478
479pub fn get_options(opts: &mut [Opt], args: &[String]) -> Result<(),Error> {
481 let args: Vec<&str> = args.iter().map(|x| x.as_ref()).collect();
482 get_options_str(opts, &args)
483}
484
485pub fn get_options_str(opts: &mut [Opt], args: &[&str]) -> Result<(),Error> {
489 let mut iter = args.iter();
490 while let Some(arg) = iter.next() {
491 if *arg == "--" {
492 handle_free_arg(arg, opts)?;
493 while let Some(arg) = iter.next() {
494 handle_free_arg(arg, opts)?;
495 return Ok(());
496 }
497 }
498 else if arg.starts_with("--") {
499 let mut chars = arg.chars();
500 chars.next();
501 chars.next();
502 let arg = chars.as_str();
503 handle_long_opt(&mut iter, arg, opts)?;
504 }
505 else if arg.starts_with("-") {
506 let mut chars = arg.chars();
507 chars.next();
508 let arg = chars.as_str();
509 handle_short_opt(&mut iter, arg, opts)?;
510 }
511 else {
512 handle_free_arg(arg, opts)?;
513 }
514 }
515 Ok(())
516}
517
518#[cfg(test)]
519mod tests {
520 use super::*;
521
522 use std::cell::RefCell;
523
524 #[test]
525 fn test_no_input() {
526 let args = [];
527 let result = get_options_str(&mut [], &args);
528 assert!(result.is_ok());
529 }
530
531 #[test]
532 fn test_short_switch() {
533 let args = ["-a"];
534
535 let mut a_opt = false;
536
537 let result = get_options_str(&mut [
538 Opt::Switch("a", &mut a_opt),
539 ], &args);
540
541 assert!(result.is_ok());
542 assert!(a_opt);
543 }
544
545 #[test]
546 fn test_short_switch_correct() {
547 let args = ["-c"];
548
549 let mut a_opt = false;
550 let mut b_opt = false;
551
552 let result = get_options_str(&mut [
553 Opt::Switch("a", &mut a_opt),
554 Opt::Switch("b", &mut b_opt),
555 ], &args);
556
557 assert!(!a_opt);
558 assert!(!b_opt);
559 assert!(result.is_err());
560 }
561
562 #[test]
563 fn test_long_switch() {
564 let args = ["--long-flag"];
565
566 let mut long_flag = false;
567 let mut another_flag = false;
568
569 let result = get_options_str(&mut [
570 Opt::Switch("long-flag", &mut long_flag),
571 Opt::Switch("another-flag", &mut another_flag),
572 ], &args);
573
574 assert!(result.is_ok());
575 assert!(long_flag);
576 assert!(!another_flag);
577 }
578
579 #[test]
580 fn test_long_and_short_switch() {
581 let args = ["-a", "--long-flag"];
582
583 let mut short_flag = false;
584 let mut another_short_flag = false;
585 let mut long_flag = false;
586 let mut another_flag = false;
587
588 let result = get_options_str(&mut [
589 Opt::Switch("a", &mut short_flag),
590 Opt::Switch("2", &mut another_short_flag),
591 Opt::Switch("long-flag", &mut long_flag),
592 Opt::Switch("another-flag", &mut another_flag),
593 ], &args);
594
595 assert!(result.is_ok());
596 assert!(long_flag);
597 assert!(!another_flag);
598 }
599
600 #[test]
601 fn test_short_or_long_flag() {
602 let args = ["--a-flag"];
603
604 let mut flag = false;
605 let mut flag2 = false;
606
607 let result = get_options_str(&mut [
608 Opt::Switch("a|a-flag", &mut flag),
609 Opt::Switch("b|b-flag", &mut flag2),
610 ], &args);
611
612 assert!(result.is_ok());
613 assert!(flag);
614 assert!(!flag2);
615
616 let args = ["-a"];
617
618 let mut flag = false;
619 let mut flag2 = false;
620
621 let result = get_options_str(&mut [
622 Opt::Switch("a|a-flag", &mut flag),
623 Opt::Switch("b|b-flag", &mut flag2),
624 ], &args);
625
626 assert!(result.is_ok());
627 assert!(flag);
628 assert!(!flag2);
629 }
630
631 #[test]
632 fn test_option_argument() {
633 let args = ["-a", "foo bar"];
634
635 let mut opt = String::new();
636
637 let result = get_options_str(&mut [
638 Opt::Arg("a|a-flag", &mut opt),
639 ], &args);
640
641 assert!(result.is_ok());
642 assert_eq!(opt, "foo bar");
643
644 let args = ["--a-flag", "foo bar"];
645
646 let mut opt = String::new();
647
648 let result = get_options_str(&mut [
649 Opt::Arg("a|a-flag", &mut opt),
650 ], &args);
651
652 assert!(result.is_ok());
653 assert_eq!(opt, "foo bar");
654 }
655
656 #[test]
657 fn test_option_argument_take_last() {
658 let args = ["-a", "foo bar", "--a-flag", "bar baz"];
659
660 let mut opt = String::new();
661
662 let result = get_options_str(&mut [
663 Opt::Arg("a|a-flag", &mut opt),
664 ], &args);
665
666 assert!(result.is_ok());
667 assert_eq!(opt, "bar baz");
668
669 let args = ["--a-flag", "foo bar", "-a", "bar baz"];
670
671 let mut opt = String::new();
672
673 let result = get_options_str(&mut [
674 Opt::Arg("a|a-flag", &mut opt),
675 ], &args);
676
677 assert!(result.is_ok());
678 assert_eq!(opt, "bar baz");
679 }
680
681 #[test]
682 fn test_option_argument_underflow() {
683 let args = ["-a"];
684
685 let mut opt = String::new();
686
687 let result = get_options_str(&mut [
688 Opt::Arg("a|a-flag", &mut opt),
689 ], &args);
690
691 assert_eq!(result, Err(Error::NeedArgument("-a".to_string())));
692 assert_eq!(result.unwrap_err().to_string(), "need argument for '-a'");
693
694 let args = ["--a-flag"];
695
696 let mut opt = String::new();
697
698 let result = get_options_str(&mut [
699 Opt::Arg("a|a-flag", &mut opt),
700 ], &args);
701
702 assert_eq!(result, Err(Error::NeedArgument("--a-flag".to_string())));
703 }
704
705 #[test]
706 fn test_callback() {
707 let args = ["-f", "foo", "-f", "bar"];
708
709 let opt = RefCell::new(String::new());
710
711 let result = get_options_str(&mut [
712 Opt::SubArg("flag|f", &|_arg, val| { opt.replace_with(|opt| format!("{}{}", opt, val)); }),
713 ], &args);
714
715 assert_eq!(result, Ok(()));
716 assert_eq!(opt.into_inner(), "foobar");
717
718 let args = ["--flag", "bar", "--flag", "foo"];
719
720 let opt = RefCell::new(String::new());
721
722 let result = get_options_str(&mut [
723 Opt::SubArg("flag|f", &|_arg, val| { opt.replace_with(|opt| format!("{}{}", opt, val)); }),
724 ], &args);
725
726 assert_eq!(result, Ok(()));
727 assert_eq!(opt.into_inner(), "barfoo");
728 }
729
730 #[test]
731 fn test_callback_switch() {
732 let args = ["-v", "-f", "-v", "-e", "-g"];
733
734 let opt: RefCell<i64> = 0.into();
735
736 let sub: &dyn for<'a> Fn(&'a str) = &|arg| {
737 if arg == "f" {
738 *opt.borrow_mut() += 2;
739 }
740 else if arg == "g" {
741 *opt.borrow_mut() -= 3;
742 }
743 };
744
745 let result = get_options_str(&mut [
746 Opt::SubSwitch("verbose|v", &mut |_arg| {
747 *opt.borrow_mut() += 1;
748 }),
749 Opt::SubSwitch("e", &mut |_arg| {
750 *opt.borrow_mut() += 4;
751 }),
752 Opt::SubSwitch("f|g", sub),
753 ], &args);
754
755 assert_eq!(result, Ok(()));
756 assert_eq!(opt, 5.into());
757 }
758
759 #[test]
760 fn test_long_equals() {
761 let args = ["--a-flag=boofar"];
762
763 let mut opt = String::new();
764
765 let result = get_options_str(&mut [
766 Opt::Arg("a|a-flag", &mut opt),
767 ], &args);
768
769 assert_eq!(result, Ok(()));
770 assert_eq!(opt, "boofar");
771 }
772
773 #[test]
774 fn test_free_arguments() {
775 let args = ["a", "bb", "ccc"];
776
777 let free = RefCell::new(vec![]);
778
779 let sub: FnFree = &|s| {
780 free.borrow_mut().push(s.to_string());
781 };
782
783 let result = get_options_str(&mut [
784 Opt::Free(sub),
785 ], &args);
786
787 assert_eq!(result, Ok(()));
788 assert_eq!(free.into_inner(), vec!["a", "bb", "ccc"]);
789 }
790
791 #[test]
792 fn test_double_dash_no_omit() {
793 let args = ["--"];
794
795 let free = RefCell::new(vec![]);
796
797 let sub: FnFree = &|s| {
798 free.borrow_mut().push(s.to_string());
799 };
800
801 let result = get_options_str(&mut [
802 Opt::Free(sub),
803 ], &args);
804
805 assert_eq!(result, Ok(()));
806 assert!(free.into_inner().len() == 1);
807 }
808
809 #[test]
810 fn test_double_dash_escape() {
811 let args = ["--", "-f"];
812
813 let mut flag = false;
814 let free = RefCell::new(vec![]);
815
816 let sub: FnFree = &|s| {
817 free.borrow_mut().push(s.to_string());
818 };
819
820 let result = get_options_str(&mut [
821 Opt::Switch("f|flag", &mut flag),
822 Opt::Free(sub),
823 ], &args);
824
825 let free = free.into_inner();
826 assert_eq!(result, Ok(()));
827 assert!(!flag);
828 assert!(free.len() == 2);
829 assert_eq!(free, vec!("--", "-f"));
830 }
831
832 #[test]
833 fn test_switch_bundling() {
834 let args = ["-ab"];
835
836 let mut flag_a = false;
837 let mut flag_b = false;
838
839 let result = get_options_str(&mut [
840 Opt::Switch("a", &mut flag_a),
841 Opt::Switch("b", &mut flag_b),
842 ], &args);
843
844 assert!(result.is_ok());
845 assert!(flag_a);
846 assert!(flag_b);
847 }
848
849 #[test]
850 fn test_switch_arg_bundling() {
851 let args = ["-ab", "2"];
852
853 let mut flag_a = false;
854 let mut flag_b = String::new();
855
856 let result = get_options_str(&mut [
857 Opt::Switch("a", &mut flag_a),
858 Opt::Arg("b", &mut flag_b),
859 ], &args);
860
861 assert!(result.is_ok());
862 assert!(flag_a);
863 assert_eq!(flag_b, "2");
864 }
865
866 #[test]
867 fn test_arg_nospace() {
868 let args = ["-a7"];
869
870 let mut flag_a = String::new();
871
872 let result = get_options_str(&mut [
873 Opt::Arg("a", &mut flag_a),
874 ], &args);
875
876 assert!(result.is_ok());
877 assert_eq!(flag_a, "7");
878 }
879
880 #[test]
881 fn test_arg_nospace_longer() {
882 let args = ["-aFooBarBaz"];
883
884 let mut flag_a = String::new();
885
886 let result = get_options_str(&mut [
887 Opt::Arg("a", &mut flag_a),
888 ], &args);
889
890 assert!(result.is_ok());
891 assert_eq!(flag_a, "FooBarBaz");
892 }
893
894 #[test]
895 fn test_bundle_switch_arg_nospace() {
896 let args = ["-ba8"];
897
898 let mut flag_a = String::new();
899 let mut flag_b = false;
900
901 let result = get_options_str(&mut [
902 Opt::Arg("a", &mut flag_a),
903 Opt::Switch("b", &mut flag_b),
904 ], &args);
905
906 assert!(result.is_ok());
907 assert_eq!(flag_a, "8");
908 assert!(flag_b);
909 }
910
911 #[test]
912 fn test_bundle_arg_switch_nospace() {
913 let args = ["-ab8"];
914
915 let mut flag_a = String::new();
916 let mut flag_b = false;
917
918 let result = get_options_str(&mut [
919 Opt::Arg("a", &mut flag_a),
920 Opt::Switch("b", &mut flag_b),
921 ], &args);
922
923 assert!(result.is_ok());
924 assert_eq!(flag_a, "b8");
925 assert!(!flag_b);
926 }
927
928 #[test]
929 fn test_flag_invalid() {
930 let args = ["-f"];
931
932 let mut flag_a = false;
933
934 let result = get_options_str(&mut [
935 Opt::Switch("a", &mut flag_a),
936 ], &args);
937
938 assert_eq!(result, Err(Error::Unexpected("-f".to_string())));
939 assert!(!flag_a);
940
941 let args = ["--foo", "7"];
942
943 let mut flag_a = false;
944 let free = RefCell::new(vec![]);
945
946 let sub: FnFree = &|s| {
947 free.borrow_mut().push(s.to_string());
948 };
949
950 let result = get_options_str(&mut [
951 Opt::Switch("a", &mut flag_a),
952 Opt::Free(sub),
953 ], &args);
954
955 assert_eq!(result, Err(Error::Unexpected("--foo".to_string())));
956 assert_eq!(result.unwrap_err().to_string(), "unexpected argument '--foo'");
957 assert_eq!(free.into_inner().len(), 0);
958 assert!(!flag_a);
959 }
960
961 #[test]
962 fn test_undeclared_free() {
963 let args = ["foo", "bar", "baz"];
964
965 let mut flag_a = false;
966
967 let result = get_options_str(&mut [
968 Opt::Switch("a", &mut flag_a),
969 ], &args);
970
971 assert_eq!(result, Err(Error::Unexpected("foo".to_string())));
972 assert!(!flag_a);
973
974 let args = ["--", "-7"];
975
976 let mut flag_a = false;
977
978 let result = get_options_str(&mut [
979 Opt::Switch("a", &mut flag_a),
980 ], &args);
981
982 assert!(!flag_a);
983 assert_eq!(result, Err(Error::Unexpected("-7".to_string())));
984 assert_eq!(result.unwrap_err().to_string(), "unexpected argument '-7'");
985 }
986
987 #[test]
988 fn test_callback_underfloaw() {
989 let args = ["--foo", "7", "--bar"];
990
991 let flag_a = RefCell::new(String::new());
992 let flag_b = RefCell::new(String::new());
993
994 let result = get_options_str(&mut [
995 Opt::SubArg("foo", &mut |_arg, val| {
996 flag_a.replace_with(|_| val.to_owned());
997 }),
998 Opt::SubArg("bar", &mut |_arg, val| {
999 flag_b.replace_with(|_| val.to_owned());
1000 }),
1001 ], &args);
1002
1003 assert_eq!(result, Err(Error::NeedArgument("--bar".to_string())));
1004 assert_eq!(flag_b.into_inner(), "");
1005 }
1006
1007 #[test]
1008 fn test_mutually_exclusive() {
1009 let args = ["--foo", "--baz", "--bar", "--baz"];
1010
1011 let command = RefCell::new(String::new());
1012
1013 let sub: &dyn for<'a> Fn(&'a str) = &|arg| {
1014 let mut command = command.borrow_mut();
1015 *command = format!("command-{}", arg);
1016 };
1017
1018 let result = get_options_str(&mut [
1019 Opt::SubSwitch("foo", sub),
1020 Opt::SubSwitch("bar", sub),
1021 Opt::SubSwitch("baz", sub),
1022 ], &args);
1023
1024 assert_eq!(result, Ok(()));
1025 assert_eq!(command.into_inner(), "command-baz");
1026 }
1027
1028 #[test]
1029 fn test_sledgehammer_example() {
1030 let args: Vec<String> = vec!["/usr/local/bin/backup-profile", "--list", "-n", "--backup-dir=/mnt/usr1/backups", "--backup-dir", "/mnt/usr1/backups-test", "-v", "--", "-f.txt"].into_iter().map(|x| x.to_string()).collect();
1033 let prog = args[0].clone();
1034 let args: Vec<String> = args.into_iter().skip(1).collect();
1035 let usage = || {
1036 let prog = std::path::Path::new(&prog).file_name().unwrap().to_string_lossy();
1037 println!("usage: {prog} [-hlcpnv] [-b BACKUP] [--] [TAR_ARGUMENTS]");
1038 println!(" backup firefox profiles on win32 from cygwin");
1039 println!(" -h,--help display this usage");
1040 println!(" -b,--backup-dir BACKUP output backup files to BACKUP (default '.')");
1041 println!(" -l,--list list profiles and their paths");
1042 println!(" -n,--dry-run perform dry run with commands printed");
1043 println!(" -p,--print print Firefox configuration root");
1044 println!(" -v,--verbose be noisier");
1045 };
1046
1047 let mut backup_dir = String::new();
1048 let mut dry_run = false;
1049 let free_args = RefCell::new(vec![]);
1050 let mut list = false;
1051 let mut print = false;
1052 let mut verbose = false;
1053
1054 let sub: FnFree = &|s| {
1055 free_args.borrow_mut().push(s.to_string());
1056 };
1057
1058 let result = get_options(&mut [
1059 Opt::SubSwitch("help|h", &mut |_| { usage(); std::process::exit(0); }),
1060 Opt::Switch ("list|l", &mut list),
1061 Opt::Switch ("p|print", &mut print),
1062 Opt::Switch ("dry-run|n", &mut dry_run),
1063 Opt::Arg ("backup-dir|b", &mut backup_dir),
1064 Opt::Switch ("verbose|v", &mut verbose),
1065 Opt::Free (sub),
1066 ], &args);
1067
1068 if let Err(ref err) = result {
1069 eprintln!("ERROR: parsing command line arguments: {err}");
1070 }
1072
1073 assert_eq!(backup_dir, "/mnt/usr1/backups-test");
1076 assert!(dry_run);
1077 assert_eq!(free_args.into_inner(), vec!["--", "-f.txt"]);
1078 assert!(list);
1079 assert!(!print);
1080 assert!(verbose);
1081
1082 assert_eq!(result, Ok(()));
1083 }
1084}
1085
1086