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