1use clap::{Arg, ArgAction, Command};
2
3#[derive(Debug, Clone)]
9pub struct LoopOptions {
10 pub command: Vec<String>,
11 pub cwd: Option<String>,
12 pub include: Option<Vec<String>>,
13 pub exclude: Option<Vec<String>>,
14 pub include_only: Option<Vec<String>>,
15 pub exclude_only: Option<Vec<String>>,
16 pub include_pattern: Option<String>,
17 pub exclude_pattern: Option<String>,
18 pub init: bool,
19}
20
21pub fn parse_args() -> LoopOptions {
26 let matches = Command::new("loop")
27 .about("Loop through directories and execute a command")
28 .version(env!("CARGO_PKG_VERSION"))
29 .author("Matt")
30 .arg(
31 Arg::new("command")
32 .required_unless_present("init")
33 .num_args(1..)
34 .help("The command to execute in each directory"),
35 )
36 .arg(
37 Arg::new("cwd")
38 .short('C')
39 .long("cwd")
40 .help("The current working directory"),
41 )
42 .arg(
43 Arg::new("include")
44 .short('i')
45 .long("include")
46 .num_args(1..)
47 .help("Additional directories to include"),
48 )
49 .arg(
50 Arg::new("exclude")
51 .short('e')
52 .long("exclude")
53 .num_args(1..)
54 .help("Directories to exclude"),
55 )
56 .arg(
57 Arg::new("include_only")
58 .long("include-only")
59 .num_args(1..)
60 .help("Only include these directories"),
61 )
62 .arg(
63 Arg::new("exclude_only")
64 .long("exclude-only")
65 .num_args(1..)
66 .help("Exclude all directories except these"),
67 )
68 .arg(
69 Arg::new("include_pattern")
70 .long("include-pattern")
71 .help("A pattern to include directories"),
72 )
73 .arg(
74 Arg::new("exclude_pattern")
75 .long("exclude-pattern")
76 .help("A pattern to exclude directories"),
77 )
78 .arg(
79 Arg::new("init")
80 .long("init")
81 .action(ArgAction::SetTrue)
82 .help("Initialize the loop configuration"),
83 )
84 .get_matches();
85
86 LoopOptions {
87 command: matches
88 .get_many::<String>("command")
89 .map(|v| v.cloned().collect())
90 .unwrap_or_default(),
91 cwd: matches.get_one::<String>("cwd").cloned(),
92 include: matches
93 .get_many::<String>("include")
94 .map(|v| v.cloned().collect()),
95 exclude: matches
96 .get_many::<String>("exclude")
97 .map(|v| v.cloned().collect()),
98 include_only: matches
99 .get_many::<String>("include_only")
100 .map(|v| v.cloned().collect()),
101 exclude_only: matches
102 .get_many::<String>("exclude_only")
103 .map(|v| v.cloned().collect()),
104 include_pattern: matches.get_one::<String>("include_pattern").cloned(),
105 exclude_pattern: matches.get_one::<String>("exclude_pattern").cloned(),
106 init: matches.get_flag("init"),
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_args_parsing() {
116 let args = parse_args_from(&["loop", "gst", "--include", "~/bootstrap"]);
117 assert_eq!(args.command, vec!["gst"]);
118 assert_eq!(args.include, Some(vec!["~/bootstrap".to_string()]));
119 assert!(args.exclude.is_none());
120 assert!(!args.init);
121 }
122
123 #[test]
124 fn test_init_flag() {
125 let args = parse_args_from(&["loop", "--init"]);
126 assert!(args.init);
127 assert!(args.command.is_empty());
128 }
129
130 fn parse_args_from(args: &[&str]) -> LoopOptions {
132 let matches = Command::new("loop")
133 .arg(
134 Arg::new("command")
135 .required_unless_present("init")
136 .num_args(1..),
137 )
138 .arg(Arg::new("cwd").short('C').long("cwd"))
139 .arg(Arg::new("include").short('i').long("include").num_args(1..))
140 .arg(Arg::new("exclude").short('e').long("exclude").num_args(1..))
141 .arg(Arg::new("include_only").long("include-only").num_args(1..))
142 .arg(Arg::new("exclude_only").long("exclude-only").num_args(1..))
143 .arg(Arg::new("include_pattern").long("include-pattern"))
144 .arg(Arg::new("exclude_pattern").long("exclude-pattern"))
145 .arg(Arg::new("init").long("init").action(ArgAction::SetTrue))
146 .try_get_matches_from(args)
147 .unwrap();
148
149 LoopOptions {
150 command: matches
151 .get_many::<String>("command")
152 .map(|v| v.cloned().collect())
153 .unwrap_or_default(),
154 cwd: matches.get_one::<String>("cwd").cloned(),
155 include: matches
156 .get_many::<String>("include")
157 .map(|v| v.cloned().collect()),
158 exclude: matches
159 .get_many::<String>("exclude")
160 .map(|v| v.cloned().collect()),
161 include_only: matches
162 .get_many::<String>("include_only")
163 .map(|v| v.cloned().collect()),
164 exclude_only: matches
165 .get_many::<String>("exclude_only")
166 .map(|v| v.cloned().collect()),
167 include_pattern: matches.get_one::<String>("include_pattern").cloned(),
168 exclude_pattern: matches.get_one::<String>("exclude_pattern").cloned(),
169 init: matches.get_flag("init"),
170 }
171 }
172}