use crate::version as version_lib;
use std::num::NonZero;
use std::path::PathBuf;
use std::sync::Arc;
use crate::{Result, env, logger, settings::Settings};
use clap::Parser;
use clx::progress::ProgressOutput;
mod builtins;
mod cache;
mod check;
mod completion;
mod config;
mod fix;
mod init;
mod install;
mod migrate;
mod run;
mod test;
mod uninstall;
mod usage;
mod util;
mod validate;
mod version;
#[derive(clap::Parser)]
#[clap(name = "hk", version = env!("CARGO_PKG_VERSION"), about = env!("CARGO_PKG_DESCRIPTION"), version = version_lib::version())]
struct Cli {
#[clap(long, global = true, value_name = "PATH", hide = true)]
hkrc: Option<PathBuf>,
#[clap(short, long, global = true)]
jobs: Option<NonZero<usize>>,
#[clap(short, long, global = true)]
profile: Vec<String>,
#[clap(short, long, global = true)]
slow: bool,
#[clap(short, long, global = true, action = clap::ArgAction::Count, overrides_with_all = ["quiet", "silent"])]
verbose: u8,
#[clap(short, long, global = true)]
no_progress: bool,
#[clap(short, long, global = true, overrides_with_all = ["verbose", "silent"])]
quiet: bool,
#[clap(long, global = true, overrides_with_all = ["quiet", "verbose"])]
silent: bool,
#[clap(long, global = true)]
trace: bool,
#[clap(long, global = true)]
json: bool,
#[clap(subcommand)]
command: Commands,
}
#[derive(clap::Subcommand)]
enum Commands {
Builtins(Box<builtins::Builtins>),
Cache(Box<cache::Cache>),
Check(Box<check::Check>),
Completion(Box<completion::Completion>),
Config(Box<config::Config>),
Fix(Box<fix::Fix>),
Init(Box<init::Init>),
Install(Box<install::Install>),
Migrate(Box<migrate::Migrate>),
Run(Box<run::Run>),
Test(Box<test::Test>),
Uninstall(Box<uninstall::Uninstall>),
Usage(Box<usage::Usage>),
Util(Box<util::Util>),
Validate(Box<validate::Validate>),
Version(Box<version::Version>),
}
pub async fn run() -> Result<()> {
let args = Cli::parse();
let mut level: Option<log::LevelFilter> = None;
Settings::set_cli_snapshot(crate::settings::CliSnapshot {
hkrc: args.hkrc,
jobs: args.jobs.map(|n| n.get()),
profiles: args.profile.clone(),
slow: args.slow,
quiet: args.quiet,
silent: args.silent,
trace: args.trace,
});
if is_ci::cached() || !console::user_attended_stderr() || args.no_progress {
clx::progress::set_output(ProgressOutput::Text);
}
if args.verbose > 1 {
clx::progress::set_output(ProgressOutput::Text);
level = Some(log::LevelFilter::Trace);
}
if args.verbose == 1 {
clx::progress::set_output(ProgressOutput::Text);
level = Some(log::LevelFilter::Debug);
}
if args.quiet {
clx::progress::set_output(ProgressOutput::Text);
level = Some(log::LevelFilter::Warn);
}
if args.silent {
clx::progress::set_output(ProgressOutput::Text);
level = Some(log::LevelFilter::Error);
}
let json_output = args.json || *env::HK_JSON || matches!(*env::HK_TRACE, env::TraceMode::Json);
let mut trace_enabled =
args.trace || matches!(*env::HK_TRACE, env::TraceMode::Text | env::TraceMode::Json);
let effective_level = level.unwrap_or(*env::HK_LOG);
if effective_level == log::LevelFilter::Trace {
trace_enabled = true;
}
if effective_level == log::LevelFilter::Debug || effective_level == log::LevelFilter::Trace {
clx::progress::set_output(ProgressOutput::Text);
}
logger::init(level);
if trace_enabled {
clx::progress::set_output(ProgressOutput::Text);
crate::trace::init_tracing(json_output)?;
}
let settings = if matches!(
args.command,
Commands::Builtins(_)
| Commands::Init(_)
| Commands::Migrate(_)
| Commands::Completion(_)
| Commands::Usage(_)
| Commands::Version(_)
) {
Arc::new(crate::settings::generated::settings::Settings::default())
} else {
Settings::get()
};
if !settings.terminal_progress {
clx::osc::configure(settings.terminal_progress);
}
match args.command {
Commands::Builtins(cmd) => cmd.run().await,
Commands::Cache(cmd) => cmd.run().await,
Commands::Check(cmd) => cmd.hook.run("check").await,
Commands::Completion(cmd) => cmd.run().await,
Commands::Config(cmd) => cmd.run().await,
Commands::Fix(cmd) => cmd.hook.run("fix").await,
Commands::Init(cmd) => cmd.run().await,
Commands::Install(cmd) => cmd.run().await,
Commands::Migrate(cmd) => cmd.run().await,
Commands::Run(cmd) => cmd.run().await,
Commands::Uninstall(cmd) => cmd.run().await,
Commands::Usage(cmd) => cmd.run().await,
Commands::Util(cmd) => cmd.run().await,
Commands::Validate(cmd) => cmd.run().await,
Commands::Version(cmd) => cmd.run().await,
Commands::Test(cmd) => cmd.run().await,
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
#[test]
fn test_subcommands_are_sorted() {
let cmd = Cli::command();
for subcmd in cmd.get_subcommands() {
clap_sort::assert_sorted(subcmd);
}
}
}