#![expect( clippy::multiple_crate_versions, reason = "transitive dependency version conflicts from upstream crates" )] #![expect( clippy::cargo_common_metadata, reason = "license and repository not yet configured" )] mod cli; mod error; mod export; mod fetch; mod git; mod http; mod ipc; mod model; mod platform; mod rate_limiter; mod resolver; mod ui_utils; mod utils; use std::{env, path::PathBuf}; use clap::Parser; use cli::{Cli, Commands}; use error::PakkerError; /// Search for pakker-lock.json in current directory and parent directories /// Returns the directory containing pakker-lock.json, or None if not found fn find_working_directory() -> Option { let mut current_dir = env::current_dir().ok()?; loop { let lockfile = current_dir.join("pakker-lock.json"); if lockfile.exists() { return Some(current_dir); } // Try parent directory if !current_dir.pop() { // Reached filesystem root return None; } } } #[tokio::main] async fn main() -> Result<(), PakkerError> { let cli = Cli::parse(); // Initialize logging based on verbosity level let log_level = match cli.verbose { 0 => "warn", // Default: only warnings and errors 1 => "info", // -v: info level 2 => "debug", // -vv: debug level _ => "trace", // -vvv+: trace level (most verbose) }; env_logger::Builder::from_env( env_logger::Env::default().default_filter_or(log_level), ) .format_timestamp(None) .format_module_path(false) .init(); // Search for pakker-lock.json in current directory and parent directories let working_dir = find_working_directory().unwrap_or_else(|| PathBuf::from(".")); let lockfile_path = working_dir.join("pakker-lock.json"); let config_path = working_dir.join("pakker.json"); let global_yes = cli.yes; match cli.command { Commands::Init(args) => { cli::commands::init::execute( args, global_yes, &lockfile_path, &config_path, ) }, Commands::Import(args) => { cli::commands::import::execute( args, global_yes, &lockfile_path, &config_path, ) .await }, Commands::Add(args) => { cli::commands::add::execute( args, global_yes, &lockfile_path, &config_path, ) .await }, Commands::AddPrj(args) => { cli::commands::add_prj::execute( args.curseforge, args.modrinth, args.github, args.project_type, args.side, args.strategy, args.redistributable, args.subpath, args.aliases, args.export, args.no_deps, global_yes, &lockfile_path, &config_path, ) .await }, Commands::Rm(args) => { cli::commands::rm::execute( &args, global_yes, &lockfile_path, &config_path, ) }, Commands::Update(args) => { cli::commands::update::execute( args, global_yes, &lockfile_path, &config_path, ) .await }, Commands::Ls(args) => cli::commands::ls::execute(&args, &lockfile_path), Commands::Set(args) => { cli::commands::set::execute(&args, &lockfile_path, &config_path) }, Commands::Link(args) => cli::commands::link::execute(&args, &lockfile_path), Commands::Unlink(args) => { cli::commands::unlink::execute(&args, &lockfile_path) }, Commands::Diff(args) => cli::commands::diff::execute(&args, &lockfile_path), Commands::Fetch(args) => { cli::commands::fetch::execute(args, &lockfile_path, &config_path).await }, Commands::Sync(args) => { cli::commands::sync::execute( args, global_yes, &lockfile_path, &config_path, ) .await }, Commands::Export(args) => { cli::commands::export::execute(args, &lockfile_path, &config_path).await }, Commands::Remote(args) => cli::commands::remote::execute(args).await, Commands::RemoteUpdate(args) => { cli::commands::remote_update::execute(&args) }, Commands::Status(args) => { cli::commands::status::execute( args.parallel, global_yes, &lockfile_path, &config_path, ) .await }, Commands::Inspect(args) => { cli::commands::inspect::execute( &args.projects, &lockfile_path, &config_path, ) }, Commands::Credentials(args) => { match args.subcommand { Some(cli::CredentialsSubcommand::Set(set_args)) => { cli::commands::credentials_set::execute( set_args.cf_api_key, set_args.modrinth_token, set_args.gh_access_token, ) }, None => { cli::commands::credentials::execute( args.delete, args.delete_file, args.delete_keyring, ) }, } }, Commands::Cfg(args) => { match args.subcommand { Some(cli::CfgSubcommand::Prj(prj_args)) => { cli::commands::cfg_prj::execute( &config_path, &lockfile_path, &prj_args.project, prj_args.r#type.as_deref(), prj_args.side.as_deref(), prj_args.update_strategy.as_deref(), prj_args.redistributable, prj_args.subpath, prj_args.add_alias, prj_args.remove_alias, prj_args.export, ) }, None => { cli::commands::cfg::execute( &config_path, args.name, args.version, args.description, args.author, args.mods_path, args.resource_packs_path, args.data_packs_path, args.worlds_path, args.shaders_path, ) }, } }, Commands::Fork(args) => { cli::commands::fork::execute(&args)?; Ok(()) }, } }