use clap::{Parser, Subcommand};
use kopi::commands::cache::CacheCommand;
use kopi::commands::current::CurrentCommand;
use kopi::commands::doctor::DoctorCommand;
use kopi::commands::env::EnvCommand;
use kopi::commands::global::GlobalCommand;
use kopi::commands::install::InstallCommand;
use kopi::commands::list::ListCommand;
use kopi::commands::local::LocalCommand;
use kopi::commands::setup::SetupCommand;
use kopi::commands::shell::ShellCommand;
use kopi::commands::shim::ShimCommand;
use kopi::commands::uninstall::UninstallCommand;
use kopi::commands::which::WhichCommand;
use kopi::config::new_kopi_config;
use kopi::error::{Result, format_error_chain, get_exit_code};
use kopi::logging;
#[derive(Parser)]
#[command(name = "kopi")]
#[command(author, version, about = "JDK version management tool", long_about = None)]
struct Cli {
#[arg(short, long, action = clap::ArgAction::Count, global = true)]
verbose: u8,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
#[command(visible_alias = "i")]
Install {
version: String,
#[arg(short, long)]
force: bool,
#[arg(long)]
dry_run: bool,
#[arg(long)]
no_progress: bool,
#[arg(long, value_name = "SECONDS")]
timeout: Option<u64>,
#[arg(long)]
javafx_bundled: bool,
},
#[command(visible_alias = "ls")]
List,
#[command(visible_alias = "use")]
Shell {
version: String,
#[arg(long)]
shell: Option<String>,
},
Current {
#[arg(short = 'q', long)]
quiet: bool,
#[arg(long)]
json: bool,
},
#[command(long_about = "Output environment variables for shell evaluation
Sets JAVA_HOME for the current or specified JDK version.
Examples:
eval \"$(kopi env)\" # Bash/Zsh
kopi env | source # Fish
kopi env | Invoke-Expression # PowerShell")]
Env {
version: Option<String>,
#[arg(long)]
shell: Option<String>,
#[arg(long, default_value_t = true, action = clap::ArgAction::Set)]
export: bool,
},
#[command(visible_alias = "g", alias = "default")]
Global {
version: String,
},
#[command(visible_alias = "l", alias = "pin")]
Local {
version: String,
},
#[command(visible_alias = "w")]
Which {
version: Option<String>,
#[arg(long, default_value = "java")]
tool: String,
#[arg(long)]
home: bool,
#[arg(long)]
json: bool,
},
Cache {
#[command(subcommand)]
command: CacheCommand,
},
#[command(visible_alias = "r", hide = true)]
Refresh {
#[arg(long)]
javafx_bundled: bool,
},
#[command(visible_alias = "s", hide = true)]
Search {
#[arg(value_name = "VERSION")]
version: Option<String>,
#[arg(short, long, conflicts_with = "detailed")]
compact: bool,
#[arg(short, long, conflicts_with = "compact")]
detailed: bool,
#[arg(long, conflicts_with_all = ["compact", "detailed"])]
json: bool,
#[arg(long)]
lts_only: bool,
#[arg(long)]
javafx_bundled: bool,
},
Setup {
#[arg(short, long)]
force: bool,
},
Shim {
#[command(subcommand)]
command: ShimCommand,
},
#[command(visible_alias = "u", alias = "remove")]
Uninstall {
version: Option<String>,
#[arg(short, long)]
force: bool,
#[arg(long)]
dry_run: bool,
#[arg(long)]
all: bool,
#[arg(long)]
cleanup: bool,
},
Doctor {
#[arg(long)]
json: bool,
#[arg(long, value_name = "CATEGORY")]
check: Option<String>,
},
}
fn setup_logger(cli: &Cli) {
logging::setup_logger(cli.verbose);
}
fn main() {
let cli = Cli::parse();
setup_logger(&cli);
let config = match new_kopi_config() {
Ok(config) => config,
Err(e) => {
eprintln!("{}", format_error_chain(&e));
std::process::exit(get_exit_code(&e));
}
};
let result: Result<()> = (|| {
match cli.command {
Commands::Install {
version,
force,
dry_run,
no_progress,
timeout,
javafx_bundled,
} => {
let command = InstallCommand::new(&config)?;
command.execute(
&version,
force,
dry_run,
no_progress,
timeout,
javafx_bundled,
)
}
Commands::List => {
let command = ListCommand::new(&config)?;
command.execute()
}
Commands::Shell { version, shell } => {
let command = ShellCommand::new(&config)?;
command.execute(&version, shell.as_deref())
}
Commands::Current { quiet, json } => {
let command = CurrentCommand::new(&config)?;
command.execute(quiet, json)
}
Commands::Env {
version,
shell,
export,
} => {
let command = EnvCommand::new(&config)?;
command.execute(version.as_deref(), shell.as_deref(), export)
}
Commands::Global { version } => {
let command = GlobalCommand::new(&config)?;
command.execute(&version)
}
Commands::Local { version } => {
let command = LocalCommand::new(&config)?;
command.execute(&version)
}
Commands::Which {
version,
tool,
home,
json,
} => {
let command = WhichCommand::new(&config)?;
command.execute(version.as_deref(), &tool, home, json)
}
Commands::Cache { command } => command.execute(&config),
Commands::Refresh { javafx_bundled } => {
let cache_cmd = CacheCommand::Refresh { javafx_bundled };
cache_cmd.execute(&config)
}
Commands::Search {
version,
compact,
detailed,
json,
lts_only,
javafx_bundled,
} => {
let cache_cmd = CacheCommand::Search {
version: version.unwrap_or_else(|| "latest".to_string()),
compact,
detailed,
json,
lts_only,
javafx_bundled,
java_version: false,
distribution_version: false,
};
cache_cmd.execute(&config)
}
Commands::Setup { force } => {
let command = SetupCommand::new(&config)?;
command.execute(force)
}
Commands::Shim { command } => command.execute(&config),
Commands::Uninstall {
version,
force,
dry_run,
all,
cleanup,
} => {
let command = UninstallCommand::new(&config)?;
command.execute(version.as_deref(), force, dry_run, all, cleanup)
}
Commands::Doctor { json, check } => {
let command = DoctorCommand::new(&config)?;
command.execute(json, cli.verbose > 0, check.as_deref())
}
}
})();
if let Err(e) = result {
eprintln!("{}", format_error_chain(&e));
std::process::exit(get_exit_code(&e));
}
}