btr 0.2.2

CLI to standardize build, test, and run commands across projects
Documentation
use clap::{Args, Parser, Subcommand, ValueEnum, builder::ValueHint};
use std::{fmt, path::PathBuf};

#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
pub enum Action {
    Build(CommandArgs),
    Test(CommandArgs),
    Run(CommandArgs),
    Dev(CommandArgs),
    Fmt(CommandArgs),
    Clean(CommandArgs),
    Ci(CommandArgs),
    Exec(ExecArgs),
    Parallel(ParallelArgs),
    Validate(ValidateArgs),
    Init(InitArgs),
    Templates(TemplatesArgs),
    Workspace(WorkspaceArgs),
    Watch(WatchArgs),
    Package(PackageArgs),
    Release(ReleaseArgs),
    Completions(CompletionsArgs),
    Schema,
    Manpage,
    List(ListArgs),
    Which,
    Doctor(DoctorArgs),
    Show(ShowArgs),
    Explain(ShowArgs),
    #[command(hide = true)]
    Complete(CompleteArgs),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum InitTemplate {
    Rust,
    Node,
    Pnpm,
    Yarn,
    Bun,
    Deno,
    Nextjs,
    Vite,
    Turbo,
    Nx,
    Python,
    Django,
    Fastapi,
    Flask,
    Poetry,
    Hatch,
    Pixi,
    Uv,
    Go,
    CargoWorkspace,
    JavaGradle,
    JavaMaven,
    KotlinGradle,
    Dotnet,
    PhpComposer,
    RubyBundler,
    Rails,
    Laravel,
    Terraform,
    Helm,
    DockerCompose,
    Cmake,
    CmakeNinja,
    Generic,
}

impl fmt::Display for Action {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Action::Build(_) => f.write_str("build"),
            Action::Test(_) => f.write_str("test"),
            Action::Run(_) => f.write_str("run"),
            Action::Dev(_) => f.write_str("dev"),
            Action::Fmt(_) => f.write_str("fmt"),
            Action::Clean(_) => f.write_str("clean"),
            Action::Ci(_) => f.write_str("ci"),
            Action::Exec(ExecArgs { name, .. }) => f.write_str(name),
            Action::Parallel(_) => f.write_str("parallel"),
            Action::Validate(_) => f.write_str("validate"),
            Action::Init(_) => f.write_str("init"),
            Action::Templates(_) => f.write_str("templates"),
            Action::Workspace(_) => f.write_str("workspace"),
            Action::Watch(_) => f.write_str("watch"),
            Action::Package(_) => f.write_str("package"),
            Action::Release(_) => f.write_str("release"),
            Action::Completions(_) => f.write_str("completions"),
            Action::Schema => f.write_str("schema"),
            Action::Manpage => f.write_str("manpage"),
            Action::List(_) => f.write_str("list"),
            Action::Which => f.write_str("which"),
            Action::Doctor(_) => f.write_str("doctor"),
            Action::Show(_) => f.write_str("show"),
            Action::Explain(_) => f.write_str("explain"),
            Action::Complete(_) => f.write_str("__complete"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct CommandArgs {
    #[arg(value_name = "ARGS", last = true, allow_hyphen_values = true)]
    pub args: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct ExecArgs {
    pub name: String,

    #[arg(value_name = "ARGS", last = true, allow_hyphen_values = true)]
    pub args: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct ParallelArgs {
    #[arg(required = true)]
    pub names: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct InitArgs {
    #[arg(long)]
    pub force: bool,

    #[arg(long = "import")]
    pub r#import: bool,

    #[arg(long, value_enum, default_value_t = InitTemplate::Rust)]
    pub template: InitTemplate,

    #[arg(long)]
    pub interactive: bool,

    #[arg(long)]
    pub detect: bool,

    #[arg(long)]
    pub print: bool,

    #[arg(long)]
    pub list_templates: bool,

    #[arg(long, value_name = "PATH", value_hint = ValueHint::FilePath)]
    pub template_file: Option<PathBuf>,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct TemplatesArgs {
    #[arg(long)]
    pub verbose: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct WorkspaceArgs {
    #[arg(long)]
    pub list: bool,

    #[arg(long)]
    pub changed_only: bool,

    #[arg(long, value_name = "N")]
    pub jobs: Option<usize>,

    #[arg(long)]
    pub fail_fast: bool,

    #[arg(long)]
    pub keep_going: bool,

    #[arg(long, value_enum, default_value_t = WorkspaceOrder::Path)]
    pub order: WorkspaceOrder,

    #[arg(long, value_name = "REF")]
    pub since: Option<String>,

    #[arg(long, value_name = "NAME")]
    pub name: Option<String>,

    #[arg(long = "tag", value_name = "TAG")]
    pub tags: Vec<String>,

    pub command: Option<String>,

    #[arg(value_name = "ARGS", last = true, allow_hyphen_values = true)]
    pub args: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
pub enum WatchAction {
    Build(CommandArgs),
    Test(CommandArgs),
    Run(CommandArgs),
    Dev(CommandArgs),
    Fmt(CommandArgs),
    Clean(CommandArgs),
    Ci(CommandArgs),
    Workspace(WorkspaceArgs),
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct WatchArgs {
    #[command(subcommand)]
    pub action: WatchAction,

    #[arg(long)]
    pub once: bool,

    #[arg(long, value_name = "MILLIS", default_value_t = 1000)]
    pub poll_interval: u64,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum WorkspaceOrder {
    Path,
    Name,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct PackageArgs {
    #[arg(long, value_hint = ValueHint::FilePath)]
    pub output: Option<PathBuf>,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct ReleaseArgs {
    #[arg(long, value_hint = ValueHint::FilePath)]
    pub output: Option<PathBuf>,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct CompletionsArgs {
    pub shell: CompletionShell,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum CompletionShell {
    Bash,
    Elvish,
    Fish,
    PowerShell,
    Zsh,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct ValidateArgs {
    #[arg(long)]
    pub strict: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct ListArgs {
    #[arg(long)]
    pub verbose: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct DoctorArgs {
    #[arg(long)]
    pub strict: bool,

    #[arg(long)]
    pub fix: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct ShowArgs {
    #[arg(long)]
    pub source: bool,

    #[arg(long)]
    pub tree: bool,

    pub name: String,

    #[arg(value_name = "ARGS", last = true, allow_hyphen_values = true)]
    pub args: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct CompleteArgs {
    #[arg(value_enum)]
    pub slot: CompleteSlot,

    #[arg(long, value_name = "PATH", value_hint = ValueHint::DirPath)]
    pub cwd: Option<PathBuf>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum CompleteSlot {
    Commands,
    Profiles,
    WorkspaceNames,
    WorkspaceTags,
    Shells,
}

#[derive(Debug, Parser)]
#[command(
    author,
    version,
    about = "Build, test, and run projects from hidden config"
)]
pub struct Cli {
    #[arg(long)]
    pub dry_run: bool,

    #[arg(long)]
    pub safe: bool,

    #[arg(long)]
    pub json: bool,

    #[arg(long = "json-events")]
    pub json_events: bool,

    #[arg(long, value_name = "DIR", value_hint = ValueHint::DirPath)]
    pub log_dir: Option<PathBuf>,

    #[arg(long, value_name = "NAME")]
    pub profile: Option<String>,

    #[arg(long, value_name = "PATH", value_hint = ValueHint::DirPath)]
    pub workspace: Option<PathBuf>,

    #[command(subcommand)]
    pub action: Action,
}