1use clap::Parser;
2
3#[derive(Parser, Debug, Clone)]
4#[command(author, version, about = "cargo-e is for Example.", long_about = None)]
5#[command(disable_version_flag = true)]
6pub struct Cli {
7 #[arg(
11 long,
12 value_name = "PATH",
13 help = "Path to read/write the stdout of the executed command."
14 )]
15 pub stdout: Option<std::path::PathBuf>,
16
17 #[arg(
19 long,
20 value_name = "PATH",
21 help = "Path to read/write the stderr of the executed command."
22 )]
23 pub stderr: Option<std::path::PathBuf>,
24 #[arg(
29 long,
30 num_args = 0..=1,
31 default_value_t = RunAll::NotSpecified,
32 default_missing_value ="",
33 value_parser,
34 help = "Run all optionally specifying run time (in seconds) per target. \
35 If the flag is present without a value, run forever."
36 )]
37 pub run_all: RunAll,
38
39 #[arg(long, help = "Create GIST run_report.md on exit.")]
40 pub gist: bool,
41 #[arg(long, help = "Build and run in release mode.")]
42 pub release: bool,
43 #[arg(
44 long,
45 short = 'q',
46 help = "Suppress cargo output when running the sample."
47 )]
48 pub quiet: bool,
49 #[clap(
54 long,
55 help = "If enabled, pre-build the examples before executing them."
56 )]
57 pub pre_build: bool,
58
59 #[clap(
60 long,
61 default_value_t = false,
62 help = "If enabled, execute the existing target directly."
63 )]
64 pub cached: bool,
65 #[arg(
69 long = "detached",
70 help = "Run the targets in detached mode. (cmd /c show | alacritty)"
71 )]
72 pub detached: bool,
73 #[arg(
74 long = "scan-dir",
75 value_name = "DIR",
76 help = "Scan the given directory for targets to run."
77 )]
78 pub scan_dir: Option<std::path::PathBuf>,
79 #[arg(
81 long = "filter",
82 short = 'f',
83 help = "Enable filter mode. cargo output is filtered and captured."
84 )]
85 pub filter: bool,
86 #[arg(
88 long,
89 short = 'v',
90 help = "Print version and feature flags in JSON format."
91 )]
92 pub version: bool,
93
94 #[arg(
95 long,
96 short = 't',
97 help = "Launch the text-based user interface (TUI)."
98 )]
99 pub tui: bool,
100
101 #[arg(long, short = 'w', help = "Operate on the entire workspace.")]
102 pub workspace: bool,
103
104 #[arg(
106 long = "pX",
107 default_value_t = false,
108 value_parser = clap::value_parser!(bool),
109 help = "Print the exit code of the process when run. (default: false)"
110 )]
111 pub print_exit_code: bool,
112
113 #[arg(
115 long = "pN",
116 default_value_t = false,
117 value_parser = clap::value_parser!(bool),
118 help = "Print the program name before execution. (default: false)"
119 )]
120 pub print_program_name: bool,
121
122 #[arg(
124 long = "pI",
125 default_value_t = true,
126 value_parser = clap::value_parser!(bool),
127 help = "Print the user instruction. (default: true)"
128 )]
129 pub print_instruction: bool,
130
131 #[arg(
132 long,
133 short = 'p',
134 default_value_t = true,
135 help = "Enable or disable paging (default: enabled)."
136 )]
137 pub paging: bool,
138
139 #[arg(
140 long,
141 short = 'r',
142 default_value_t = false,
143 help = "Relative numbers (default: enabled)."
144 )]
145 pub relative_numbers: bool,
146
147 #[arg(
148 long = "wait",
149 short = 'W',
150 default_value_t = 15,
151 help = "Set wait time in seconds (default: 15)."
152 )]
153 pub wait: u64,
154
155 #[arg(
157 long = "subcommand",
158 short = 's',
159 value_parser,
160 default_value = "run",
161 help = "Specify subcommands (e.g., `build|b`, `test|t`)."
162 )]
163 pub subcommand: String,
164
165 #[arg(help = "Specify an explicit target to run.")]
166 pub explicit_example: Option<String>,
167
168 #[arg(
169 long = "run-at-a-time",
170 short = 'J',
171 default_value_t = 1,
172 value_parser = clap::value_parser!(usize),
173 help = "Number of targets to run at a time in --run-all mode (--run-at-a-time)"
174 )]
175 pub run_at_a_time: usize,
176
177 #[arg(
178 long = "nS",
179 default_value_t = false,
180 help = "Disable status lines during runtime loop output."
181 )]
182 pub no_status_lines: bool,
183
184 #[arg(
185 long = "nT",
186 default_value_t = false,
187 help = "Disable text-to-speech output."
188 )]
189 pub no_tts: bool,
190
191 #[arg(
193 long = "parse-available",
194 help = "Parse available targets from stdin (one per line)."
195 )]
196 pub parse_available: bool,
197
198 #[arg(
199 long = "default-binary-is-runner",
200 default_value_t = false,
201 help = "If enabled, treat the default binary as the runner for targets."
202 )]
203 pub default_binary_is_runner: bool,
204
205 #[arg(long = "nW", default_value_t = false, help = "Disable window popups.")]
206 pub no_window: bool,
207
208 #[arg(
210 long = "log",
211 value_name = "PATH",
212 help = "Enable logging to a file at the given path, or to stdout if not specified."
213 )]
214 pub log: Option<std::path::PathBuf>,
215
216 #[arg(
217 long = "manifest-path",
218 value_name = "PATH",
219 help = "Specify the path to the Cargo.toml manifest file."
220 )]
221 pub manifest_path: Option<std::path::PathBuf>,
222
223 #[arg(
224 long = "target",
225 value_name = "TARGET",
226 help = "Specify the target triple for the build."
227 )]
228 pub target: Option<String>,
229
230 #[arg(
232 long = "json-all-targets",
233 help = "Output the list of all targets as JSON."
234 )]
235 pub json_all_targets: bool,
236
237 #[arg(last = true, help = "Additional arguments passed to the command.")]
238 pub extra: Vec<String>,
239
240 #[clap(
241 long,
242 value_name = "SECONDS",
243 help = "Time in seconds to keep detached windows open before killing."
244 )]
245 pub detached_hold: Option<u32>,
246 #[clap(
247 long,
248 value_name = "SECONDS",
249 help = "Time in seconds for detached windows to delay before executing target"
250 )]
251 pub detached_delay: Option<u32>,
252}
253
254pub fn print_version_and_features() {
256 let version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
258
259 let mut features = Vec::new();
262
263 if cfg!(feature = "tui") {
264 features.push("tui");
265 } else {
266 features.push("!tui");
267 }
268 if cfg!(feature = "concurrent") {
269 features.push("concurrent");
270 } else {
271 features.push("!concurrent");
272 }
273 if cfg!(target_os = "windows") {
274 features.push("windows");
275 } else {
276 features.push("!windows");
277 }
278 if cfg!(feature = "equivalent") {
279 features.push("equivalent");
280 } else {
281 features.push("!equivalent");
282 }
283
284 let json_features = format!(
285 "[{}]",
286 features
287 .iter()
288 .map(|f| format!("\"{}\"", f))
289 .collect::<Vec<String>>()
290 .join(", ")
291 );
292 println!("cargo-e {}", version);
293 println!("{}", json_features);
294 std::process::exit(0);
295}
296
297pub fn get_feature_flags() -> Vec<&'static str> {
300 let mut flags = Vec::new();
301 if cfg!(feature = "tui") {
302 flags.push("tui");
303 } else {
304 flags.push("!tui");
305 }
306 if cfg!(feature = "concurrent") {
307 flags.push("concurrent");
308 } else {
309 flags.push("!concurrent");
310 }
311 if cfg!(target_os = "windows") {
312 flags.push("windows");
313 } else {
314 flags.push("!windows");
315 }
316 if cfg!(feature = "equivalent") {
317 flags.push("equivalent");
318 } else {
319 flags.push("!equivalent");
320 }
321 flags
322}
323
324use std::str::FromStr;
325
326#[derive(Debug, Clone, PartialEq, Default)]
328pub enum RunAll {
329 #[default]
331 NotSpecified,
332 Forever,
334 Timeout(u64),
336}
337
338impl FromStr for RunAll {
339 type Err = String;
340 fn from_str(s: &str) -> Result<Self, Self::Err> {
341 if s.is_empty() {
343 Ok(RunAll::Forever)
344 } else if s.eq_ignore_ascii_case("not_specified") {
345 Ok(RunAll::NotSpecified)
346 } else {
347 s.parse::<u64>()
349 .map(RunAll::Timeout)
350 .map_err(|e| e.to_string())
351 }
352 }
353}
354
355impl std::fmt::Display for RunAll {
356 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
357 match self {
358 RunAll::NotSpecified => write!(f, "not_specified"),
359 RunAll::Forever => write!(f, "forever"),
360 RunAll::Timeout(secs) => write!(f, "{}", secs),
361 }
362 }
363}
364
365pub fn custom_cli(args: &mut Vec<String>) -> (Option<usize>, Vec<&String>) {
366 if args.len() > 1 && args[1].as_str() == "e" {
368 args.remove(1);
369 }
370 let mut run_at_a_time: Option<usize> = None;
371 let mut filtered_args = vec![];
373 for arg in &*args {
374 if let Some(num) = arg
375 .strip_prefix("--run-")
376 .and_then(|s| s.strip_suffix("-at-a-time"))
377 {
378 if let Ok(n) = num.parse() {
379 println!("run-at-a-time: {}", n);
380 run_at_a_time = Some(n);
381 }
382 continue;
384 }
385 filtered_args.push(arg);
386 }
387 (run_at_a_time, filtered_args)
388}