use clap::{Parser, Subcommand, ValueEnum};
use std::path::PathBuf;
#[derive(Parser, Debug)]
#[command(
name = "cleansh",
author = "Obscura Team (Relay)",
version = env!("CARGO_PKG_VERSION"),
about = "Securely redact sensitive data from text",
long_about = "Cleansh is a command-line utility for securely redacting sensitive information from text-based data. It helps you sanitize logs, code, documents, or terminal output to ensure that Personally Identifiable Information (PII) and other sensitive patterns are removed or obfuscated according to a configurable rule set.",
arg_required_else_help = true,
)]
pub struct Cli {
#[arg(long, short = 'q', help = "Suppress all informational and debug messages.")]
pub quiet: bool,
#[arg(long, short = 'd', help = "Enable debug logging.")]
pub debug: bool,
#[arg(long = "disable-debug", help = "Disable debug logging, overriding RUST_LOG.")]
pub disable_debug: bool,
#[arg(long = "theme", value_name = "FILE", help = "Specify the path to a custom YAML theme file.")]
pub theme: Option<PathBuf>,
#[arg(long = "disable-donation-prompts", help = "Disable future prompts for donations.")]
pub disable_donation_prompts: bool,
#[arg(long = "suppress-donation-prompt", help = "Suppress donation prompt for this run only (does not persist).", global = true)]
pub suppress_donation_prompt: bool,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand, Debug)]
pub enum Commands {
#[command(about = "Sanitizes an input file or stdin, redacting sensitive information.")]
Sanitize(SanitizeCommand),
#[command(about = "Scans an input for sensitive data and provides a detailed summary without redacting.")]
Scan(ScanCommand),
#[command(about = "Uninstall cleansh and remove its associated files.")]
Uninstall {
#[arg(long, short = 'y', help = "Proceed with uninstallation without a confirmation prompt.")]
yes: bool,
},
#[command(subcommand, about = "Provides a suite of tools for managing redaction profiles.")]
Profiles(ProfilesCommand),
}
#[derive(Parser, Debug)]
pub struct SanitizeCommand {
#[arg(long, short = 'i', value_name = "FILE", help = "Read input from a specified file instead of stdin.")]
pub input_file: Option<PathBuf>,
#[arg(long, short = 'o', value_name = "FILE", help = "Write output to a specified file instead of stdout.")]
pub output: Option<PathBuf>,
#[arg(long, short = 'c', help = "Copy sanitized output to the system clipboard.")]
pub clipboard: bool,
#[arg(long, short = 'D', help = "Show a unified diff to highlight the changes made.")]
pub diff: bool,
#[arg(long = "config", value_name = "FILE", help = "Path to a custom redaction configuration file (YAML).")]
pub config: Option<PathBuf>,
#[arg(long = "profile", value_name = "NAME", help = "Loads a predefined profile from the local configuration.")]
pub profile: Option<String>,
#[arg(long, short = 'e', value_delimiter = ',', help = "Explicitly enable only these rule names (comma-separated).")]
pub enable: Vec<String>,
#[arg(long, short = 'x', value_delimiter = ',', help = "Explicitly disable these rule names (comma-separated).")]
pub disable: Vec<String>,
#[arg(long = "engine", value_name = "ENGINE", default_value = "regex", help = "Select a sanitization engine (e.g., 'regex' or 'entropy').")]
pub engine: EngineChoice,
#[arg(long = "line-buffered", help = "Process input line by line (useful for streaming data from pipes).")]
pub line_buffered: bool,
#[arg(long = "no-redaction-summary", help = "Suppress the redaction summary.")]
pub no_summary: bool,
#[arg(long = "artifact-attach", value_name = "PATH", help = "Writes both the artifact JSON and the sanitized output into a single ZIP file.")]
pub artifact_attach: Option<PathBuf>,
#[arg(long = "artifact-out", value_name = "PATH", help = "Specifies the output path for the artifact JSON.")]
pub artifact_out: Option<PathBuf>,
#[arg(long = "artifact-key", value_name = "PATH", help = "Signs the canonical JSON blob using an RSA private key specified by this flag.")]
pub artifact_key: Option<PathBuf>,
}
#[derive(Parser, Debug)]
pub struct ScanCommand {
#[arg(long, short = 'i', value_name = "FILE", help = "Read input from a specified file instead of stdin.")]
pub input_file: Option<PathBuf>,
#[arg(long = "config", value_name = "FILE", help = "Path to a custom redaction configuration file (YAML).")]
pub config: Option<PathBuf>,
#[arg(long = "profile", value_name = "NAME", help = "Loads a predefined profile from the local configuration.")]
pub profile: Option<String>,
#[arg(long = "rules", value_name = "NAME", default_value = "default", help = "Select the rule set to use (defaults to 'default').")]
pub rules: String,
#[arg(long = "enable", short = 'e', value_delimiter = ',', help = "Explicitly enable only these rule names (comma-separated).")]
pub enable: Vec<String>,
#[arg(long = "disable", short = 'x', value_delimiter = ',', help = "Explicitly disable these rule names (comma-separated).")]
pub disable: Vec<String>,
#[arg(long = "fail-over-threshold", value_name = "N", help = "Exit with a non-zero code if the total number of detected secrets exceeds this threshold.")]
pub fail_over_threshold: Option<usize>,
#[arg(long = "json-file", value_name = "FILE", help = "Export the redaction statistics to a JSON file.")]
pub json_file: Option<PathBuf>,
#[arg(long = "json-stdout", conflicts_with = "json_file", help = "Export the redaction statistics to stdout as JSON.")]
pub json_stdout: bool,
#[arg(long = "sample-matches", value_name = "N", help = "Display a sample of up to N unique matches per rule in the console output.")]
pub sample_matches: Option<usize>,
}
#[derive(Parser, Debug)]
pub struct VerifyArtifactCommand {
#[arg(long = "verify-artifact", value_name = "FILE", help = "Checks the cryptographic signature of an artifact JSON file.")]
pub verify_artifact: PathBuf,
#[arg(long = "public-key", value_name = "PATH", help = "Provides the public key necessary to verify the signature.")]
pub public_key: PathBuf,
}
#[derive(Parser, Debug)]
pub struct SyncProfilesCommand {
#[arg(long = "org-id", value_name = "ID", help = "The unique identifier for the organization to sync profiles from.")]
pub org_id: String,
#[arg(long = "org-key", value_name = "KEY", help = "Provides the API key for authenticating with the profile server.")]
pub org_key: String,
}
#[derive(Subcommand, Debug)]
pub enum ProfilesCommand {
#[command(about = "Signs a profile YAML file using a key from a file.")]
Sign {
#[arg(value_name = "FILE", help = "The path to the profile YAML file to sign.")]
path: PathBuf,
#[arg(long = "key", value_name = "KEY_FILE", help = "The path to the key file for signing.")]
key_file: PathBuf,
},
#[command(about = "Verifies the signature of a profile YAML file.")]
Verify {
#[arg(value_name = "FILE", help = "The path to the profile YAML file to verify.")]
path: PathBuf,
#[arg(long = "public-key", value_name = "PUB_KEY_FILE", help = "The path to the public key for verification.")]
pub_key_file: PathBuf,
},
#[command(about = "Lists all available local profiles.")]
List,
}
#[derive(Debug, Clone, ValueEnum, PartialEq)]
pub enum EngineChoice {
Regex,
Entropy,
}