use clap::{ArgGroup, Parser, Subcommand};
use std::path::PathBuf;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
#[arg(long, global = true, help = "Enable debug logging output.")]
pub debug: bool,
#[arg(long, short = 'n', global = true, help = "Disable emoji output.")]
pub no_emojis: bool,
}
#[derive(Subcommand, Debug)]
pub enum Commands {
#[command(about = "Generates Ed25519 signatures and/or traditional checksum files.")]
Sign(SignArgs),
#[command(about = "Verifies files using Ed25519 signatures OR traditional checksum files.")]
Verify(VerifyArgs),
#[command(about = "Manage trusted public keys and the keyring.")]
Trust(TrustArgs),
#[command(
about = "Configure default settings for sigmate.",
long_about = "Run with arguments to set values directly, or run without arguments for an interactive setup session."
)]
Configure(ConfigureArgs),
#[command(
about = "Removes generated signature artifacts, always prompting for confirmation.",
long_about = "If a PATH is provided, this command inspects that directory for artifacts. If no PATH is provided, it defaults to cleaning the './signatures' directory and any default checksum files from the current directory."
)]
Clean(CleanArgs),
}
#[derive(Parser, Debug)]
#[command(group(ArgGroup::new("input").required(true).args(&["target_path_or_file", "walk", "file_list"])))]
pub struct SignArgs {
#[arg(value_name = "TARGET", help = "Path to the file or directory to process.")]
pub target_path_or_file: Option<PathBuf>,
#[arg(long, value_name = "DIR", help = "Directory to recursively process.")]
pub walk: Option<PathBuf>,
#[arg(long = "list", value_name = "FILE", help = "Text file listing files/directories to process.")]
pub file_list: Option<PathBuf>,
#[arg(long, value_name = "PATH", help = "Path to private key (PEM). Overrides configured default.")]
pub key: Option<PathBuf>,
#[arg(long, value_name = "ENV_VAR", help = "Environment variable for the private key password.")]
pub key_password_env: Option<String>,
#[arg(long, value_name = "DIR", help = "Base directory for all generated files.")]
pub signatures_output: Option<PathBuf>,
#[arg(long, help = "Output raw Ed25519 .sig file.")]
pub raw: bool,
#[arg(long, help = "Output sigmate.meta.json.")]
pub meta: bool,
#[arg(long, help = "Output both .sig and sigmate.meta.json.")]
pub both: bool,
#[arg(long, help = "Generate CycloneDX SBOM (sigmate.sbom.json).")]
pub sbom: bool,
#[arg(long, short, help = "Print a JSON summary of operations.")]
pub json: bool,
#[arg(long, value_name = "IDENTITY", help = "Override signer identity. Overrides configured default.")]
pub identity: Option<String>,
#[arg(long, value_name = "HOST", help = "Override host name for metadata.")]
pub host: Option<String>,
#[arg(long, value_name = "HOURS", help = "Expiration for signatures in hours (e.g., 72).")]
pub expires_in: Option<u64>,
#[arg(long, help = "Exclude absolute file paths in metadata and SBOMs.")]
pub no_abspath: bool,
#[arg(long, help = "Generate MD5SUMS file.")]
pub gen_md5sums: bool,
#[arg(long, help = "Generate SHA1SUMS file.")]
pub gen_sha1sums: bool,
#[arg(long, help = "Generate SHA256SUMS file.")]
pub gen_sha256sums: bool,
#[arg(long, help = "Generate SHA512SUMS file.")]
pub gen_sha512sums: bool,
#[arg(long, help = "Overwrite existing signature and checksum artifacts.")]
pub force: bool,
#[arg(long, help = "Detect and report orphan signature artifacts and stale metadata.")]
pub report_orphans: bool,
#[arg(long, help = "Remove orphan signature artifacts and stale metadata after confirmation.")]
pub prune_orphans: bool,
#[arg(long, short = 'y', help = "Auto-confirm pruning actions without prompting.")]
pub yes: bool,
}
#[derive(Parser, Debug)]
#[command(group(ArgGroup::new("verify-input").args(&["target_path_or_file", "walk", "file_list"])))]
pub struct VerifyArgs {
#[arg(value_name = "TARGET", help = "Path to the file or directory to verify.")]
pub target_path_or_file: Option<PathBuf>,
#[arg(long, value_name = "PATH", help = "Path to a public key (PEM) for verification.", conflicts_with = "signer")]
pub key: Option<PathBuf>,
#[arg(long, value_name = "NAME", help = "Verify using a trusted signer's name from the keyring.")]
pub signer: Option<String>,
#[arg(long, value_name = "FILE", help = "Explicit Ed25519 signature file (.sig).")]
pub signature: Option<PathBuf>,
#[arg(long, value_name = "TYPE", default_value = "auto", value_parser = ["auto", "raw", "meta"])]
pub sig_type: String,
#[arg(long, value_name = "DIR", help = "Directory containing Ed25519 signature files.")]
pub signatures_input: Option<PathBuf>,
#[arg(long, help = "Enforce that the signer's public key is in the trust store and 'verified'.")]
pub require_trusted: bool,
#[arg(long, value_name = "FILE", help = "Path to the checksum file for verification.")]
pub checksum_file: Option<PathBuf>,
#[arg(long, value_name = "ALGO", default_value = "auto", value_parser = ["auto", "md5", "sha1", "sha256", "sha512"])]
pub checksum_algo: String,
#[arg(long, value_name = "FORMAT", default_value = "auto", value_parser = ["auto", "gnu", "bsd"])]
pub checksum_format: String,
#[arg(long, value_name = "DIR", help = "Directory to recursively verify.")]
pub walk: Option<PathBuf>,
#[arg(long = "list", value_name = "FILE", help = "Text file listing files/directories to verify.")]
pub file_list: Option<PathBuf>,
#[arg(long, short, help = "Output result as structured JSON.")]
pub json: bool,
#[arg(long, help = "Use relative paths in JSON output.")]
pub no_abspath: bool,
}
#[derive(Parser, Debug)]
pub struct TrustArgs {
#[command(subcommand)]
pub command: TrustCommands,
}
#[derive(Subcommand, Debug)]
pub enum TrustCommands {
#[command(about = "Adds a key to the trust store and keyring.")]
Add(TrustAddArgs),
#[command(about = "Shows all keys in the trust store.")]
List(TrustListArgs),
#[command(about = "Changes the verification status of a key.")]
Update(TrustUpdateArgs),
#[command(about = "Removes a key from the trust store.")]
Remove(TrustRemoveArgs),
}
#[derive(Parser, Debug)]
pub struct TrustAddArgs {
#[arg(value_name = "KEYFILE", help = "Path to the public key file to add.")]
pub keyfile: PathBuf,
#[arg(long, help = "A unique, memorable name (alias) for this key.")]
pub name: String,
#[arg(long, help = "Signer organization (optional).")]
pub org: Option<String>,
#[arg(long, help = "User or entity adding this key to the store.")]
pub added_by: String,
#[arg(long, help = "Overwrite an existing key alias in the keyring.")]
pub force: bool,
}
#[derive(Parser, Debug)]
pub struct TrustListArgs {
#[arg(long, help = "Display keys in JSON format.")]
pub json: bool,
}
#[derive(Parser, Debug)]
pub struct TrustUpdateArgs {
#[arg(value_name = "FINGERPRINT", help = "The fingerprint of the key to update.")]
pub fingerprint: String,
#[arg(long, value_parser = ["pending", "verified", "revoked", "compromised"], help = "New verification status.")]
pub status: String,
#[arg(long, help = "User or entity updating the key's status.")]
pub updated_by: String,
#[arg(long, help = "Optional notes for this status update.")]
pub notes: Option<String>,
}
#[derive(Parser, Debug)]
pub struct TrustRemoveArgs {
#[arg(value_name = "FINGERPRINT", help = "The fingerprint of the key to remove.")]
pub fingerprint: String,
}
#[derive(Parser, Debug)]
pub struct ConfigureArgs {
#[arg(long, value_name = "PATH", help = "Set the default private key path for signing.")]
pub private_key_path: Option<PathBuf>,
#[arg(long, value_name = "IDENTITY", help = "Set the default signer identity (e.g., 'Name <email@host.com>').")]
pub signer_identity: Option<String>,
#[arg(long, value_name = "PATH", help = "Set the default public key keyring path.")]
pub keyring_path: Option<PathBuf>,
#[arg(long, short, help = "Output result as structured JSON.")]
pub json: bool,
}
#[derive(Parser, Debug)]
pub struct CleanArgs {
#[arg(value_name = "PATH", help = "Path to the artifact directory to clean.")]
pub path: Option<PathBuf>,
}