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
66 #[arg(
68 long = "filter",
69 short = 'f',
70 help = "Enable filter mode. cargo output is filtered and captured."
71 )]
72 pub filter: bool,
73 #[arg(
75 long,
76 short = 'v',
77 help = "Print version and feature flags in JSON format."
78 )]
79 pub version: bool,
80
81 #[arg(
82 long,
83 short = 't',
84 help = "Launch the text-based user interface (TUI)."
85 )]
86 pub tui: bool,
87
88 #[arg(long, short = 'w', help = "Operate on the entire workspace.")]
89 pub workspace: bool,
90
91 #[arg(
93 long = "pX",
94 default_value_t = false,
95 value_parser = clap::value_parser!(bool),
96 help = "Print the exit code of the process when run. (default: false)"
97 )]
98 pub print_exit_code: bool,
99
100 #[arg(
102 long = "pN",
103 default_value_t = false,
104 value_parser = clap::value_parser!(bool),
105 help = "Print the program name before execution. (default: false)"
106 )]
107 pub print_program_name: bool,
108
109 #[arg(
111 long = "pI",
112 default_value_t = true,
113 value_parser = clap::value_parser!(bool),
114 help = "Print the user instruction. (default: true)"
115 )]
116 pub print_instruction: bool,
117
118 #[arg(
119 long,
120 short = 'p',
121 default_value_t = true,
122 help = "Enable or disable paging (default: enabled)."
123 )]
124 pub paging: bool,
125
126 #[arg(
127 long,
128 short = 'r',
129 default_value_t = false,
130 help = "Relative numbers (default: enabled)."
131 )]
132 pub relative_numbers: bool,
133
134 #[arg(
135 long = "wait",
136 short = 'W',
137 default_value_t = 15,
138 help = "Set wait time in seconds (default: 15)."
139 )]
140 pub wait: u64,
141
142 #[arg(
144 long = "subcommand",
145 short = 's',
146 value_parser,
147 default_value = "run",
148 help = "Specify subcommands (e.g., `build|b`, `test|t`)."
149 )]
150 pub subcommand: String,
151
152 #[arg(help = "Specify an explicit target to run.")]
153 pub explicit_example: Option<String>,
154
155 #[arg(
156 long = "run-at-a-time",
157 short = 'J',
158 default_value_t = 1,
159 value_parser = clap::value_parser!(usize),
160 help = "Number of targets to run at a time in --run-all mode (--run-at-a-time)"
161 )]
162 pub run_at_a_time: usize,
163
164 #[arg(
165 long = "nS",
166 default_value_t = false,
167 help = "Disable status lines during runtime loop output."
168 )]
169 pub no_status_lines: bool,
170
171 #[arg(
172 long = "nT",
173 default_value_t = false,
174 help = "Disable text-to-speech output."
175 )]
176 pub no_tts: bool,
177
178 #[arg(
180 long = "parse-available",
181 help = "Parse available targets from stdin (one per line)."
182 )]
183 pub parse_available: bool,
184
185 #[arg(
186 long = "default-binary-is-runner",
187 default_value_t = false,
188 help = "If enabled, treat the default binary as the runner for targets."
189 )]
190 pub default_binary_is_runner: bool,
191
192 #[arg(long = "nW", default_value_t = false, help = "Disable window popups.")]
193 pub no_window: bool,
194
195 #[arg(
197 long = "log",
198 value_name = "PATH",
199 help = "Enable logging to a file at the given path, or to stdout if not specified."
200 )]
201 pub log: Option<std::path::PathBuf>,
202
203 #[arg(
204 long = "manifest-path",
205 value_name = "PATH",
206 help = "Specify the path to the Cargo.toml manifest file."
207 )]
208 pub manifest_path: Option<std::path::PathBuf>,
209
210 #[arg(
211 long = "target",
212 value_name = "TARGET",
213 help = "Specify the target triple for the build."
214 )]
215 pub target: Option<String>,
216
217 #[arg(
219 long = "json-all-targets",
220 help = "Output the list of all targets as JSON."
221 )]
222 pub json_all_targets: bool,
223
224 #[arg(last = true, help = "Additional arguments passed to the command.")]
225 pub extra: Vec<String>,
226}
227
228pub fn print_version_and_features() {
230 let version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
232
233 let mut features = Vec::new();
236
237 if cfg!(feature = "tui") {
238 features.push("tui");
239 } else {
240 features.push("!tui");
241 }
242 if cfg!(feature = "concurrent") {
243 features.push("concurrent");
244 } else {
245 features.push("!concurrent");
246 }
247 if cfg!(target_os = "windows") {
248 features.push("windows");
249 } else {
250 features.push("!windows");
251 }
252 if cfg!(feature = "equivalent") {
253 features.push("equivalent");
254 } else {
255 features.push("!equivalent");
256 }
257
258 let json_features = format!(
259 "[{}]",
260 features
261 .iter()
262 .map(|f| format!("\"{}\"", f))
263 .collect::<Vec<String>>()
264 .join(", ")
265 );
266 println!("cargo-e {}", version);
267 println!("{}", json_features);
268 std::process::exit(0);
269}
270
271pub fn get_feature_flags() -> Vec<&'static str> {
274 let mut flags = Vec::new();
275 if cfg!(feature = "tui") {
276 flags.push("tui");
277 } else {
278 flags.push("!tui");
279 }
280 if cfg!(feature = "concurrent") {
281 flags.push("concurrent");
282 } else {
283 flags.push("!concurrent");
284 }
285 if cfg!(target_os = "windows") {
286 flags.push("windows");
287 } else {
288 flags.push("!windows");
289 }
290 if cfg!(feature = "equivalent") {
291 flags.push("equivalent");
292 } else {
293 flags.push("!equivalent");
294 }
295 flags
296}
297
298use std::str::FromStr;
299
300#[derive(Debug, Clone, PartialEq, Default)]
302pub enum RunAll {
303 #[default]
305 NotSpecified,
306 Forever,
308 Timeout(u64),
310}
311
312impl FromStr for RunAll {
313 type Err = String;
314 fn from_str(s: &str) -> Result<Self, Self::Err> {
315 if s.is_empty() {
317 Ok(RunAll::Forever)
318 } else if s.eq_ignore_ascii_case("not_specified") {
319 Ok(RunAll::NotSpecified)
320 } else {
321 s.parse::<u64>()
323 .map(RunAll::Timeout)
324 .map_err(|e| e.to_string())
325 }
326 }
327}
328
329impl std::fmt::Display for RunAll {
330 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
331 match self {
332 RunAll::NotSpecified => write!(f, "not_specified"),
333 RunAll::Forever => write!(f, "forever"),
334 RunAll::Timeout(secs) => write!(f, "{}", secs),
335 }
336 }
337}
338
339pub fn custom_cli(args: &mut Vec<String>) -> (Option<usize>, Vec<&String>) {
340 if args.len() > 1 && args[1].as_str() == "e" {
342 args.remove(1);
343 }
344 let mut run_at_a_time: Option<usize> = None;
345 let mut filtered_args = vec![];
347 for arg in &*args {
348 if let Some(num) = arg
349 .strip_prefix("--run-")
350 .and_then(|s| s.strip_suffix("-at-a-time"))
351 {
352 if let Ok(n) = num.parse() {
353 println!("run-at-a-time: {}", n);
354 run_at_a_time = Some(n);
355 }
356 continue;
358 }
359 filtered_args.push(arg);
360 }
361 (run_at_a_time, filtered_args)
362}