cargo_e/
e_cli.rs

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