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    /// If provided with a value (e.g. `--run-all 10`), each target will run for 10 seconds.
10    /// If provided without a value (i.e. just `--run-all`), it means run forever.
11    /// If not provided at all, then the default wait time is used.
12    #[arg(
13        long,
14        num_args = 0..=1,
15        default_value_t = RunAll::NotSpecified,
16        default_missing_value ="",
17        value_parser,
18        help = "Run all optionally specifying run time (in seconds) per target. \
19                If the flag is present without a value, run forever."
20    )]
21    pub run_all: RunAll,
22
23    #[arg(long, help = "Create GIST run_report.md on exit.")]
24    pub gist: bool,
25    #[arg(long, help = "Build and run in release mode.")]
26    pub release: bool,
27    #[arg(
28        long,
29        short = 'q',
30        help = "Suppress cargo output when running the sample."
31    )]
32    pub quiet: bool,
33    // /// Comma-separated list of package names.
34    // #[clap(long, value_delimiter = ',', help = "Optional list of package names to run examples for. If omitted, defaults to ALL_PACKAGES.")]
35    // pub specified_packages: Vec<String>,
36    /// Pre-build examples before running.
37    #[clap(
38        long,
39        help = "If enabled, pre-build the examples before executing them."
40    )]
41    pub pre_build: bool,
42
43    /// Enable passthrough mode (no cargo output filtering, stdout is captured).
44    #[arg(
45        long = "filter",
46        short = 'f',
47        help = "Enable passthrough mode. No cargo output is filtered, and stdout is captured."
48    )]
49    pub filter: bool,
50    /// Print version and feature flags in JSON format.
51    #[arg(
52        long,
53        short = 'v',
54        help = "Print version and feature flags in JSON format."
55    )]
56    pub version: bool,
57
58    #[arg(
59        long,
60        short = 't',
61        help = "Launch the text-based user interface (TUI)."
62    )]
63    pub tui: bool,
64
65    #[arg(long, short = 'w', help = "Operate on the entire workspace.")]
66    pub workspace: bool,
67
68    /// Print the exit code of the process when run.
69    #[arg(
70        long = "pX",
71        default_value_t = false,
72        value_parser = clap::value_parser!(bool),
73        help = "Print the exit code of the process when run. (default: false)"
74    )]
75    pub print_exit_code: bool,
76
77    /// Print the program name before execution.
78    #[arg(
79        long = "pN",
80        default_value_t = false,
81        value_parser = clap::value_parser!(bool),
82        help = "Print the program name before execution. (default: false)"
83    )]
84    pub print_program_name: bool,
85
86    /// Print the program name before execution.
87    #[arg(
88        long = "pI",
89        default_value_t = true,
90        value_parser = clap::value_parser!(bool),
91        help = "Print the user instruction. (default: true)"
92    )]
93    pub print_instruction: bool,
94
95    #[arg(
96        long,
97        short = 'p',
98        default_value_t = true,
99        help = "Enable or disable paging (default: enabled)."
100    )]
101    pub paging: bool,
102
103    #[arg(
104        long,
105        short = 'r',
106        default_value_t = false,
107        help = "Relative numbers (default: enabled)."
108    )]
109    pub relative_numbers: bool,
110
111    #[arg(
112        long = "wait",
113        short = 'W',
114        default_value_t = 15,
115        help = "Set wait time in seconds (default: 15)."
116    )]
117    pub wait: u64,
118
119    /// Subcommands to run (e.g., `build|b`, `test|t`).
120    #[arg(
121        long = "subcommand",
122        short = 's',
123        value_parser,
124        default_value = "run",
125        help = "Specify subcommands (e.g., `build|b`, `test|t`)."
126    )]
127    pub subcommand: String,
128
129    #[arg(help = "Specify an explicit target to run.")]
130    pub explicit_example: Option<String>,
131
132    #[arg(last = true, help = "Additional arguments passed to the command.")]
133    pub extra: Vec<String>,
134}
135
136/// Print the version and the JSON array of feature flags.
137pub fn print_version_and_features() {
138    // Print the version string.
139    let version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
140
141    // Build a list of feature flags. Enabled features are printed normally,
142    // while disabled features are prefixed with an exclamation mark.
143    let mut features = Vec::new();
144
145    if cfg!(feature = "tui") {
146        features.push("tui");
147    } else {
148        features.push("!tui");
149    }
150    if cfg!(feature = "concurrent") {
151        features.push("concurrent");
152    } else {
153        features.push("!concurrent");
154    }
155    if cfg!(target_os = "windows") {
156        features.push("windows");
157    } else {
158        features.push("!windows");
159    }
160    if cfg!(feature = "equivalent") {
161        features.push("equivalent");
162    } else {
163        features.push("!equivalent");
164    }
165
166    let json_features = format!(
167        "[{}]",
168        features
169            .iter()
170            .map(|f| format!("\"{}\"", f))
171            .collect::<Vec<String>>()
172            .join(", ")
173    );
174    println!("cargo-e {}", version);
175    println!("{}", json_features);
176    std::process::exit(0);
177}
178
179/// Returns a vector of feature flag strings.  
180/// Enabled features are listed as-is while disabled ones are prefixed with "!".
181pub fn get_feature_flags() -> Vec<&'static str> {
182    let mut flags = Vec::new();
183    if cfg!(feature = "tui") {
184        flags.push("tui");
185    } else {
186        flags.push("!tui");
187    }
188    if cfg!(feature = "concurrent") {
189        flags.push("concurrent");
190    } else {
191        flags.push("!concurrent");
192    }
193    if cfg!(target_os = "windows") {
194        flags.push("windows");
195    } else {
196        flags.push("!windows");
197    }
198    if cfg!(feature = "equivalent") {
199        flags.push("equivalent");
200    } else {
201        flags.push("!equivalent");
202    }
203    flags
204}
205
206use std::str::FromStr;
207
208/// Represents the state of the `--run-all` flag.
209#[derive(Debug, Clone, PartialEq, Default)]
210pub enum RunAll {
211    /// The flag was not specified.
212    #[default]
213    NotSpecified,
214    /// The flag was specified without a value—indicating “run forever.”
215    Forever,
216    /// The flag was specified with a timeout value.
217    Timeout(u64),
218}
219
220impl FromStr for RunAll {
221    type Err = String;
222    fn from_str(s: &str) -> Result<Self, Self::Err> {
223        // An empty string means the flag was provided without a value → run forever.
224        if s.is_empty() {
225            Ok(RunAll::Forever)
226        } else if s.eq_ignore_ascii_case("not_specified") {
227            Ok(RunAll::NotSpecified)
228        } else {
229            // Otherwise, try parsing a u64 value.
230            s.parse::<u64>()
231                .map(RunAll::Timeout)
232                .map_err(|e| e.to_string())
233        }
234    }
235}
236
237impl std::fmt::Display for RunAll {
238    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239        match self {
240            RunAll::NotSpecified => write!(f, "not_specified"),
241            RunAll::Forever => write!(f, "forever"),
242            RunAll::Timeout(secs) => write!(f, "{}", secs),
243        }
244    }
245}