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