pakker/src/cli.rs
NotAShelf fa5befff3b
cli: add --all, --updates, --no-deps flags to commands
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I25581b8de945284b4ce7c2c85601a86f6a6a6964
2026-02-19 00:22:45 +03:00

629 lines
13 KiB
Rust

pub mod commands;
use clap::{Args, Parser, Subcommand};
use crate::model::{
enums::{ProjectSide, ProjectType, UpdateStrategy},
fork::RefType,
};
#[derive(Parser)]
#[clap(name = "pakker")]
#[clap(about = "A multiplatform modpack manager for Minecraft", long_about = None)]
pub struct Cli {
/// Enable verbose output (-v for info, -vv for debug, -vvv for trace)
#[clap(short, long, action = clap::ArgAction::Count)]
pub verbose: u8,
#[clap(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
/// Initialize a new modpack project
Init(InitArgs),
/// Import an existing modpack
Import(ImportArgs),
/// Add projects to the modpack
Add(AddArgs),
/// Add projects with explicit platform specification (non-interactive)
#[clap(name = "add-prj", alias = "prj")]
AddPrj(AddPrjArgs),
/// Remove projects from the modpack
Rm(RmArgs),
/// Update projects
Update(UpdateArgs),
/// List projects in the modpack
Ls(LsArgs),
/// Set project properties
Set(SetArgs),
/// Link projects together
Link(LinkArgs),
/// Unlink projects
Unlink(UnlinkArgs),
/// Show differences between local and remote
Diff(DiffArgs),
/// Fetch project files
Fetch(FetchArgs),
/// Sync projects (fetch + update)
Sync(SyncArgs),
/// Export modpack
Export(ExportArgs),
/// Manage remote repositories
Remote(RemoteArgs),
/// Update modpack from remote Git repository
RemoteUpdate(RemoteUpdateArgs),
/// Check for available updates
Status(StatusArgs),
/// Inspect project details
Inspect(InspectArgs),
/// Manage API credentials
Credentials(CredentialsArgs),
/// Configure modpack properties
Cfg(CfgArgs),
/// Manage fork configuration
Fork(ForkArgs),
}
#[derive(Args)]
pub struct InitArgs {
/// Modpack name
#[clap(short, long)]
pub name: Option<String>,
/// Modpack version
#[clap(short = 'V', long)]
pub version: Option<String>,
/// Target platform
#[clap(short, long)]
pub target: Option<String>,
/// Minecraft versions (space-separated)
#[clap(short, long = "mc-versions", value_delimiter = ' ', num_args = 1..)]
pub mc_versions: Option<Vec<String>>,
/// Mod loaders (format: name=version, can be specified multiple times)
#[clap(short, long = "loaders", value_delimiter = ',')]
pub loaders: Option<Vec<String>>,
/// Skip interactive prompts (use defaults)
#[clap(short, long)]
pub yes: bool,
}
#[derive(Args)]
pub struct ImportArgs {
/// Path to modpack file
pub file: String,
/// Resolve dependencies
#[clap(short = 'D', long = "deps")]
pub deps: bool,
/// Skip confirmation prompts
#[clap(short, long)]
pub yes: bool,
}
#[derive(Args)]
pub struct AddArgs {
/// Project identifiers to add
#[clap(required = true)]
pub inputs: Vec<String>,
/// Project type (mod, resourcepack, shader, datapack, world)
#[clap(short = 't', long = "type")]
pub project_type: Option<ProjectType>,
/// Skip resolving dependencies
#[clap(short = 'D', long)]
pub no_deps: bool,
/// Update if already exists
#[clap(short, long)]
pub update: bool,
/// Skip confirmation prompts
#[clap(short, long)]
pub yes: bool,
}
#[derive(Args)]
pub struct AddPrjArgs {
/// `CurseForge` project slug or ID (optional file ID: `slug#file_id`)
#[clap(long = "cf", alias = "curseforge")]
pub curseforge: Option<String>,
/// Modrinth project slug or ID (optional file ID: `slug#file_id`)
#[clap(long = "mr", alias = "modrinth")]
pub modrinth: Option<String>,
/// GitHub repository (format: owner/repo or owner/repo#tag)
#[clap(long = "gh", alias = "github")]
pub github: Option<String>,
/// Project type (mod, resourcepack, shader, datapack, world)
#[clap(short = 't', long = "type")]
pub project_type: Option<ProjectType>,
/// Project side (client, server, both)
#[clap(long)]
pub side: Option<ProjectSide>,
/// Update strategy (latest, none)
#[clap(long)]
pub strategy: Option<UpdateStrategy>,
/// Redistributable flag
#[clap(long)]
pub redistributable: Option<bool>,
/// Subpath for project file placement
#[clap(long)]
pub subpath: Option<String>,
/// Project aliases (can be specified multiple times)
#[clap(long = "alias")]
pub aliases: Vec<String>,
/// Export flag (whether to include in exports)
#[clap(long)]
pub export: Option<bool>,
/// Skip resolving dependencies
#[clap(short = 'D', long = "no-deps")]
pub no_deps: bool,
/// Skip confirmation prompts
#[clap(short, long)]
pub yes: bool,
}
#[derive(Args)]
pub struct RmArgs {
/// Project identifiers to remove
#[clap(required = true)]
pub inputs: Vec<String>,
/// Remove all projects
#[clap(short = 'a', long)]
pub all: bool,
/// Skip confirmation prompt
#[clap(short, long)]
pub yes: bool,
/// Skip removing dependent projects
#[clap(short = 'D', long = "no-deps")]
pub no_deps: bool,
}
#[derive(Args)]
pub struct UpdateArgs {
/// Projects to update (empty = all)
#[arg(value_name = "PROJECT")]
pub inputs: Vec<String>,
/// Update all projects
#[arg(short, long)]
pub all: bool,
/// Skip confirmation prompts
#[arg(short, long)]
pub yes: bool,
}
#[derive(Args)]
pub struct LsArgs {
/// Show detailed information
#[clap(short, long)]
pub detailed: bool,
/// Add update information for projects
#[clap(short = 'c', long = "check-updates")]
pub check_updates: bool,
/// Maximum length for project names
#[clap(long = "name-max-length")]
pub name_max_length: Option<usize>,
}
#[derive(Args)]
pub struct SetArgs {
/// Project identifier (optional for lockfile properties)
pub input: Option<String>,
/// Project type
#[clap(long)]
pub r#type: Option<String>,
/// Project side (client/server/both)
#[clap(long)]
pub side: Option<String>,
/// Update strategy (latest/none)
#[clap(long)]
pub strategy: Option<String>,
/// Redistributable flag
#[clap(long)]
pub redistributable: Option<bool>,
/// Change the target of the pack (curseforge, modrinth, multiplatform)
#[clap(short = 't', long)]
pub target: Option<String>,
/// Change the minecraft versions (comma-separated)
#[clap(short = 'v', long)]
pub mc_versions: Option<String>,
/// Change the mod loaders (format: name=version,name=version)
#[clap(short = 'l', long)]
pub loaders: Option<String>,
}
#[derive(Args)]
pub struct LinkArgs {
/// Source project
pub from: String,
/// Target project
pub to: String,
}
#[derive(Args)]
pub struct UnlinkArgs {
/// Source project
pub from: String,
/// Target project
pub to: String,
}
#[derive(Args)]
pub struct DiffArgs {
/// Path to old lockfile
pub old_lockfile: String,
/// Path to current lockfile (optional, defaults to pakku-lock.json)
pub current_lockfile: Option<String>,
/// Export markdown diff
#[clap(long)]
pub markdown_diff: Option<String>,
/// Export markdown (formatted)
#[clap(long)]
pub markdown: Option<String>,
/// Verbose output (show file changes)
#[clap(short, long)]
pub verbose: bool,
/// Header size for markdown (0-5)
#[clap(short = 'H', long, default_value = "2")]
pub header_size: usize,
}
#[derive(Args)]
pub struct FetchArgs {
/// Timeout for waiting on conflicting operations (seconds)
#[clap(short, long)]
pub timeout: Option<u64>,
/// Number of retry attempts for failed downloads
#[clap(short = 'r', long, default_value = "2")]
pub retry: u32,
/// Move unknown files to shelf instead of deleting
#[clap(long)]
pub shelve: bool,
}
#[derive(Args)]
pub struct SyncArgs {
/// Sync additions only
#[clap(short = 'A', long)]
pub additions: bool,
/// Sync removals only
#[clap(short = 'R', long)]
pub removals: bool,
/// Sync updates only (apply pending updates)
#[clap(short = 'U', long)]
pub updates: bool,
}
#[derive(Args)]
pub struct ExportArgs {
/// Export profile (curseforge, modrinth, serverpack)
/// If not specified, all profiles will be exported
#[clap(short, long)]
pub profile: Option<String>,
/// Output directory
#[clap(short, long)]
pub output: Option<String>,
/// Use Pakker-compatible output layout (build/<profile>/...)
/// Default is Pakker layout (exports/...)
#[clap(long)]
pub pakker_layout: bool,
/// Show file IO errors during export
#[clap(long = "show-io-errors")]
pub show_io_errors: bool,
/// Export modpack without server content
/// Modrinth: exclude server-overrides and SERVER mods
/// `ServerPack`: skip export
#[clap(long = "no-server")]
pub no_server: bool,
}
#[derive(Args)]
pub struct RemoteArgs {
/// Git URL to install from (if empty, shows status)
pub url: Option<String>,
/// Branch to checkout (instead of remote's HEAD)
#[clap(short, long)]
pub branch: Option<String>,
/// Install server pack
#[clap(short = 'S', long)]
pub server_pack: bool,
/// Retry count for downloads
#[clap(short, long, default_value = "2")]
pub retry: u32,
/// Remove remote from modpack
#[clap(long = "rm", long = "remove")]
pub remove: bool,
}
#[derive(Args)]
pub struct RemoteUpdateArgs {
/// Branch to checkout instead of remote's HEAD
#[clap(short, long)]
pub branch: Option<String>,
/// Install server pack instead of full modpack
#[clap(short, long)]
pub server_pack: bool,
}
#[derive(Args)]
pub struct StatusArgs {
/// Check updates in parallel
#[clap(short, long)]
pub parallel: bool,
}
#[derive(Args)]
pub struct InspectArgs {
/// Project identifiers to inspect
#[clap(required = true)]
pub projects: Vec<String>,
}
#[derive(Args)]
pub struct CredentialsArgs {
/// Delete stored credentials (defaults to deleting both file and keyring)
#[clap(short, long)]
pub delete: bool,
/// Delete credentials file (~/.pakku/credentials)
#[clap(long)]
pub delete_file: bool,
/// Delete credentials from keyring (service: pakker)
#[clap(long)]
pub delete_keyring: bool,
#[clap(subcommand)]
pub subcommand: Option<CredentialsSubcommand>,
}
#[derive(Subcommand)]
pub enum CredentialsSubcommand {
/// Set API credentials
Set(CredentialsSetArgs),
}
#[derive(Args)]
pub struct CredentialsSetArgs {
/// `CurseForge` API key
#[clap(long)]
pub cf_api_key: Option<String>,
/// Modrinth API token
#[clap(long)]
pub modrinth_token: Option<String>,
/// GitHub access token
#[clap(long)]
pub gh_access_token: Option<String>,
}
#[derive(Args)]
pub struct CfgArgs {
/// Modpack name
#[clap(long)]
pub name: Option<String>,
/// Modpack version
#[clap(long)]
pub version: Option<String>,
/// Modpack description
#[clap(long)]
pub description: Option<String>,
/// Modpack author
#[clap(long)]
pub author: Option<String>,
/// Path for mods
#[clap(long)]
pub mods_path: Option<String>,
/// Path for resource packs
#[clap(long)]
pub resource_packs_path: Option<String>,
/// Path for data packs
#[clap(long)]
pub data_packs_path: Option<String>,
/// Path for worlds
#[clap(long)]
pub worlds_path: Option<String>,
/// Path for shaders
#[clap(long)]
pub shaders_path: Option<String>,
#[clap(subcommand)]
pub subcommand: Option<CfgSubcommand>,
}
#[derive(Subcommand)]
pub enum CfgSubcommand {
/// Configure per-project settings
Prj(CfgPrjArgs),
}
#[derive(Args)]
pub struct CfgPrjArgs {
/// Project identifier
pub project: String,
/// Project type
#[clap(long)]
pub r#type: Option<String>,
/// Project side (client/server/both)
#[clap(long)]
pub side: Option<String>,
/// Update strategy (latest/none)
#[clap(long)]
pub update_strategy: Option<String>,
/// Redistributable flag
#[clap(long)]
pub redistributable: Option<bool>,
/// Subpath for project
#[clap(long)]
pub subpath: Option<String>,
/// Add alias
#[clap(long)]
pub add_alias: Option<String>,
/// Remove alias
#[clap(long)]
pub remove_alias: Option<String>,
/// Export flag
#[clap(long)]
pub export: Option<bool>,
}
/// Fork subcommand arguments
#[derive(Debug, Args)]
#[command(args_conflicts_with_subcommands = true)]
pub struct ForkArgs {
#[clap(subcommand)]
pub subcommand: ForkSubcommand,
}
#[derive(Debug, Subcommand)]
pub enum ForkSubcommand {
/// Initialize fork from parent repository
Init {
/// Git URL of parent repository
#[clap(long, conflicts_with = "from_path")]
git_url: Option<String>,
/// Use current repository as parent
#[clap(long, conflicts_with = "from_path")]
from_current: bool,
/// Use an already-cloned repository as parent (path to worktree or .git)
#[clap(long, value_parser, conflicts_with_all = &["git_url", "from_current"])]
from_path: Option<String>,
/// Branch/tag/commit to track
#[clap(long)]
ref_name: Option<String>,
/// Type of ref (branch/tag/commit)
#[clap(long, value_enum)]
ref_type: Option<RefType>,
/// Remote name
#[clap(long, default_value = "origin")]
remote: Option<String>,
},
/// Update fork configuration
Set {
/// New git URL (optional)
#[clap(long)]
git_url: Option<String>,
/// Branch/tag/commit to track
#[clap(long)]
ref_name: String,
/// Type of ref (branch/tag/commit)
#[clap(long, value_enum)]
ref_type: Option<RefType>,
/// Remote name
#[clap(long)]
remote: Option<String>,
},
/// Show fork configuration
Show,
/// Remove fork configuration
Unset,
/// Sync with parent repository
Sync,
/// Promote projects to parent (legacy)
Promote {
/// Project identifiers to promote
projects: Vec<String>,
},
}