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 #[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 #[clap(
38 long,
39 help = "If enabled, pre-build the examples before executing them."
40 )]
41 pub pre_build: bool,
42
43 #[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 #[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 #[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 #[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 #[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 #[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
136pub fn print_version_and_features() {
138 let version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
140
141 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
179pub 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#[derive(Debug, Clone, PartialEq, Default)]
210pub enum RunAll {
211 #[default]
213 NotSpecified,
214 Forever,
216 Timeout(u64),
218}
219
220impl FromStr for RunAll {
221 type Err = String;
222 fn from_str(s: &str) -> Result<Self, Self::Err> {
223 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 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}