use clap::Parser;
#[derive(Parser, Debug, Clone)]
#[command(author, version, about = "cargo-e is for Example.", long_about = None)]
#[command(disable_version_flag = true)]
pub struct Cli {
#[arg(
long = "cwd-wsr",
help = "Run targets from the workspace root (Cargo.toml parent) instead of the current working directory."
)]
pub cwd_wsr: bool,
#[arg(
long,
value_name = "PATH",
help = "Path to read/write the stdout of the executed command."
)]
pub stdout: Option<std::path::PathBuf>,
#[arg(
long,
value_name = "PATH",
help = "Path to read/write the stderr of the executed command."
)]
pub stderr: Option<std::path::PathBuf>,
#[arg(
long,
num_args = 0..=1,
default_value_t = RunAll::NotSpecified,
default_missing_value ="",
value_parser,
help = "Run all optionally specifying run time (in seconds) per target. \
If the flag is present without a value, run forever."
)]
pub run_all: RunAll,
#[arg(long, help = "Create GIST run_report.md on exit.")]
pub gist: bool,
#[arg(long, help = "Build and run in release mode.")]
pub release: bool,
#[arg(
long,
short = 'q',
help = "Suppress cargo output when running the sample."
)]
pub quiet: bool,
#[clap(
long,
help = "If enabled, pre-build the examples before executing them."
)]
pub pre_build: bool,
#[clap(
long,
default_value_t = false,
help = "If enabled, execute the existing target directly."
)]
pub cached: bool,
#[arg(
long = "detached",
help = "Run the targets in detached mode. (cmd /c show | alacritty)"
)]
pub detached: bool,
#[arg(
long = "scan-dir",
value_name = "DIR",
help = "Scan the given directory for targets to run."
)]
pub scan_dir: Option<std::path::PathBuf>,
#[arg(
long = "filter",
short = 'f',
help = "Enable filter mode. cargo output is filtered and captured."
)]
pub filter: bool,
#[arg(
long,
short = 'v',
help = "Print version and feature flags in JSON format."
)]
pub version: bool,
#[arg(
long,
short = 't',
help = "Launch the text-based user interface (TUI)."
)]
pub tui: bool,
#[arg(long, short = 'w', help = "Operate on the entire workspace.")]
pub workspace: bool,
#[arg(
long = "pX",
default_value_t = false,
value_parser = clap::value_parser!(bool),
help = "Print the exit code of the process when run. (default: false)"
)]
pub print_exit_code: bool,
#[arg(
long = "pN",
default_value_t = false,
value_parser = clap::value_parser!(bool),
help = "Print the program name before execution. (default: false)"
)]
pub print_program_name: bool,
#[arg(
long = "pI",
default_value_t = true,
value_parser = clap::value_parser!(bool),
help = "Print the user instruction. (default: true)"
)]
pub print_instruction: bool,
#[arg(
long,
short = 'p',
default_value_t = true,
help = "Enable or disable paging (default: enabled)."
)]
pub paging: bool,
#[arg(
long,
short = 'r',
default_value_t = false,
help = "Relative numbers (default: enabled)."
)]
pub relative_numbers: bool,
#[arg(
long = "wait",
short = 'W',
default_value_t = 15,
help = "Set wait time in seconds (default: 15)."
)]
pub wait: u64,
#[arg(
long = "subcommand",
short = 's',
value_parser,
default_value = "run",
help = "Specify subcommands (e.g., `build|b`, `test|t`)."
)]
pub subcommand: String,
#[arg(help = "Specify an explicit target to run.")]
pub explicit_example: Option<String>,
#[arg(
long = "run-at-a-time",
short = 'J',
default_value_t = 1,
value_parser = clap::value_parser!(usize),
help = "Number of targets to run at a time in --run-all mode (--run-at-a-time)"
)]
pub run_at_a_time: usize,
#[arg(
long = "nS",
default_value_t = false,
help = "Disable status lines during runtime loop output."
)]
pub no_status_lines: bool,
#[arg(
long = "nT",
default_value_t = false,
help = "Disable text-to-speech output."
)]
pub no_tts: bool,
#[arg(
long = "parse-available",
help = "Parse available targets from stdin (one per line)."
)]
pub parse_available: bool,
#[arg(
long = "default-binary-is-runner",
default_value_t = false,
help = "If enabled, treat the default binary as the runner for targets."
)]
pub default_binary_is_runner: bool,
#[arg(long = "nW", default_value_t = false, help = "Disable window popups.")]
pub no_window: bool,
#[arg(
long = "log",
value_name = "PATH",
help = "Enable logging to a file at the given path, or to stdout if not specified."
)]
pub log: Option<std::path::PathBuf>,
#[arg(
long = "manifest-path",
value_name = "PATH",
help = "Specify the path to the Cargo.toml manifest file."
)]
pub manifest_path: Option<std::path::PathBuf>,
#[arg(
long = "target",
value_name = "TARGET",
help = "Specify the target triple for the build."
)]
pub target: Option<String>,
#[arg(
long = "json-all-targets",
help = "Output the list of all targets as JSON."
)]
pub json_all_targets: bool,
#[arg(last = true, help = "Additional arguments passed to the command.")]
pub extra: Vec<String>,
#[clap(
long,
value_name = "SECONDS",
help = "Time in seconds to keep detached windows open before killing."
)]
pub detached_hold: Option<u32>,
#[clap(
long,
value_name = "SECONDS",
help = "Time in seconds for detached windows to delay before executing target"
)]
pub detached_delay: Option<u32>,
}
pub fn print_version_and_features() {
let version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
let mut features = Vec::new();
if cfg!(feature = "tui") {
features.push("tui");
} else {
features.push("!tui");
}
if cfg!(feature = "concurrent") {
features.push("concurrent");
} else {
features.push("!concurrent");
}
if cfg!(target_os = "windows") {
features.push("windows");
} else {
features.push("!windows");
}
if cfg!(feature = "equivalent") {
features.push("equivalent");
} else {
features.push("!equivalent");
}
let json_features = format!(
"[{}]",
features
.iter()
.map(|f| format!("\"{}\"", f))
.collect::<Vec<String>>()
.join(", ")
);
println!("cargo-e {}", version);
println!("{}", json_features);
std::process::exit(0);
}
pub fn get_feature_flags() -> Vec<&'static str> {
let mut flags = Vec::new();
if cfg!(feature = "tui") {
flags.push("tui");
} else {
flags.push("!tui");
}
if cfg!(feature = "concurrent") {
flags.push("concurrent");
} else {
flags.push("!concurrent");
}
if cfg!(target_os = "windows") {
flags.push("windows");
} else {
flags.push("!windows");
}
if cfg!(feature = "equivalent") {
flags.push("equivalent");
} else {
flags.push("!equivalent");
}
flags
}
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Default)]
pub enum RunAll {
#[default]
NotSpecified,
Forever,
Timeout(u64),
}
impl FromStr for RunAll {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
Ok(RunAll::Forever)
} else if s.eq_ignore_ascii_case("not_specified") {
Ok(RunAll::NotSpecified)
} else {
s.parse::<u64>()
.map(RunAll::Timeout)
.map_err(|e| e.to_string())
}
}
}
impl std::fmt::Display for RunAll {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RunAll::NotSpecified => write!(f, "not_specified"),
RunAll::Forever => write!(f, "forever"),
RunAll::Timeout(secs) => write!(f, "{}", secs),
}
}
}
pub fn custom_cli(args: &mut Vec<String>) -> (Option<usize>, Vec<&String>) {
if args.len() > 1 && args[1].as_str() == "e" {
args.remove(1);
}
let mut run_at_a_time: Option<usize> = None;
let mut filtered_args = vec![];
for arg in &*args {
if let Some(num) = arg
.strip_prefix("--run-")
.and_then(|s| s.strip_suffix("-at-a-time"))
{
if let Ok(n) = num.parse() {
println!("run-at-a-time: {}", n);
run_at_a_time = Some(n);
}
continue;
}
filtered_args.push(arg);
}
(run_at_a_time, filtered_args)
}