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 all examples for a given number of seconds.
8
9    /// Path to read/write the stdout of the executed command.
10    #[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    /// Path to read/write the stderr of the executed command.
18    #[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    ///
25    /// If provided with a value (e.g. `--run-all 10`), each target will run for 10 seconds.
26    /// If provided without a value (i.e. just `--run-all`), it means run forever.
27    /// If not provided at all, then the default wait time is used.
28    #[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    // /// Comma-separated list of package names.
50    // #[clap(long, value_delimiter = ',', help = "Optional list of package names to run examples for. If omitted, defaults to ALL_PACKAGES.")]
51    // pub specified_packages: Vec<String>,
52    /// Pre-build examples before running.
53    #[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    /// Scan the given directory for targets to run.
66
67    /// Run the command in detached mode.
68    #[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    /// Enable filter mode. cargo output is filtered and captured."
80    #[arg(
81        long = "filter",
82        short = 'f',
83        help = "Enable filter mode. cargo output is filtered and captured."
84    )]
85    pub filter: bool,
86    /// Print version and feature flags in JSON format.
87    #[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    /// Print the exit code of the process when run.
105    #[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    /// Print the program name before execution.
114    #[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    /// Print the program name before execution.
123    #[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    /// Subcommands to run (e.g., `build|b`, `test|t`).
156    #[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    /// Parse available targets from stdin (one per line).
192    #[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    /// Enable logging to a file or stdout.
209    #[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    /// Output the list of targets as JSON.
231    #[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
254/// Print the version and the JSON array of feature flags.
255pub fn print_version_and_features() {
256    // Print the version string.
257    let version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
258
259    // Build a list of feature flags. Enabled features are printed normally,
260    // while disabled features are prefixed with an exclamation mark.
261    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
297/// Returns a vector of feature flag strings.  
298/// Enabled features are listed as-is while disabled ones are prefixed with "!".
299pub 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/// Represents the state of the `--run-all` flag.
327#[derive(Debug, Clone, PartialEq, Default)]
328pub enum RunAll {
329    /// The flag was not specified.
330    #[default]
331    NotSpecified,
332    /// The flag was specified without a value—indicating “run forever.”
333    Forever,
334    /// The flag was specified with a timeout value.
335    Timeout(u64),
336}
337
338impl FromStr for RunAll {
339    type Err = String;
340    fn from_str(s: &str) -> Result<Self, Self::Err> {
341        // An empty string means the flag was provided without a value → run forever.
342        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            // Otherwise, try parsing a u64 value.
348            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 the first argument after the binary name is "e", remove it.
367    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    // default
372    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            // Don't push this arg to filtered_args
383            continue;
384        }
385        filtered_args.push(arg);
386    }
387    (run_at_a_time, filtered_args)
388}