use std::path::PathBuf;
use clap::{Args, ValueEnum};
#[derive(Args, Debug)]
pub struct CompletionsArgs {
#[arg(value_enum)]
pub shell: ShellType,
#[arg(
long,
help = "Install completion script to XDG data directory (Bash: ~/.local/share/bash-completion/completions/atomwrite)"
)]
pub install: bool,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum ShellType {
Bash,
Zsh,
Fish,
#[value(name = "powershell")]
PowerShell,
Elvish,
}
#[derive(Args, Debug)]
pub struct HashArgs {
#[arg(required = true)]
pub paths: Vec<PathBuf>,
#[arg(long, help = "Verify file checksum against expected BLAKE3 hash")]
pub verify: Option<String>,
#[arg(long, help = "Hash content from stdin instead of files")]
pub stdin: bool,
#[arg(short, long, help = "Recurse into directories")]
pub recursive: bool,
}
#[derive(Args, Debug)]
pub struct DeleteArgs {
#[arg(required = true)]
pub paths: Vec<PathBuf>,
#[arg(long, help = "Create backup before deleting")]
pub backup: bool,
#[arg(long, default_value_t = 5, help = "Number of backups to retain")]
pub retention: u8,
#[arg(short, long, help = "Recurse into directories")]
pub recursive: bool,
#[arg(short = 'g', long, action = clap::ArgAction::Append, help = "Include files matching glob")]
pub include: Vec<String>,
#[arg(long, action = clap::ArgAction::Append, help = "Exclude files matching glob")]
pub exclude: Vec<String>,
#[arg(long, help = "Show what would be done without deleting")]
pub dry_run: bool,
#[arg(short = 'y', long, help = "Skip confirmation")]
pub yes: bool,
}
#[derive(Args, Debug)]
pub struct CountArgs {
#[arg(default_value = ".")]
pub paths: Vec<PathBuf>,
#[arg(long, help = "Group counts by file extension")]
pub by_extension: bool,
#[arg(long, help = "Sort by file size (top N)")]
pub by_size: bool,
#[arg(long, default_value_t = 10, help = "Number of top results")]
pub top: usize,
#[arg(short = 'g', long, action = clap::ArgAction::Append, help = "Include files matching glob")]
pub include: Vec<String>,
#[arg(long, action = clap::ArgAction::Append, help = "Exclude files matching glob")]
pub exclude: Vec<String>,
}
#[derive(Args, Debug)]
pub struct DiffArgs {
pub file_a: PathBuf,
pub file_b: PathBuf,
#[arg(long, help = "Output unified diff format")]
pub unified: bool,
#[arg(long, help = "Only show summary statistics")]
pub stat: bool,
#[arg(
short = 'C',
long,
default_value_t = 3,
help = "Lines of context in unified diff"
)]
pub context: usize,
#[arg(long, value_enum, default_value_t = DiffAlgorithm::Patience, help = "Diff algorithm")]
pub algorithm: DiffAlgorithm,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum DiffAlgorithm {
Myers,
Patience,
Lcs,
}
#[derive(Args, Debug)]
pub struct MoveArgs {
pub source: PathBuf,
pub target: PathBuf,
#[arg(long, help = "Create backup of destination if it exists")]
pub backup: bool,
#[arg(long, default_value_t = 5, help = "Number of backups to retain")]
pub retention: u8,
#[arg(short, long, help = "Overwrite destination if it exists")]
pub force: bool,
#[arg(long, help = "Show what would be done without moving")]
pub dry_run: bool,
}
#[derive(Args, Debug)]
pub struct CopyArgs {
pub source: PathBuf,
pub target: PathBuf,
#[arg(long, help = "Create backup of destination if it exists")]
pub backup: bool,
#[arg(short, long, help = "Overwrite destination if it exists")]
pub force: bool,
#[arg(short, long, help = "Copy directories recursively")]
pub recursive: bool,
#[arg(long, help = "Preserve timestamps and permissions")]
pub preserve: bool,
#[arg(long, help = "Show what would be done without copying")]
pub dry_run: bool,
}
#[derive(Args, Debug)]
pub struct ReadArgs {
pub path: PathBuf,
#[arg(long, help = "Line range to read (1-based, e.g. 1:50)")]
pub lines: Option<String>,
#[arg(long, help = "Single line number with optional context")]
pub line: Option<usize>,
#[arg(
short = 'C',
long,
default_value_t = 0,
help = "Lines of context around --line"
)]
pub context: usize,
#[arg(long, help = "Read first N lines")]
pub head: Option<usize>,
#[arg(long, help = "Read last N lines")]
pub tail: Option<usize>,
#[arg(long, help = "Return only metadata (no content)")]
pub stat: bool,
#[arg(long, value_enum, default_value_t = OutputFormat::Ndjson, help = "Output format")]
pub format: OutputFormat,
#[arg(long, help = "Verify file checksum against expected BLAKE3 hash")]
pub verify_checksum: Option<String>,
#[arg(long, help = "Filter returned lines to those matching this regex")]
pub grep: Option<String>,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum OutputFormat {
Ndjson,
Raw,
}
#[derive(Args, Debug)]
pub struct WriteArgs {
pub target: PathBuf,
#[arg(long, help = "Create backup before overwriting")]
pub backup: bool,
#[arg(long, default_value_t = 5, help = "Number of backups to retain")]
pub retention: u8,
#[arg(long, help = "Maximum input size in bytes")]
pub max_size: Option<u64>,
#[arg(long, help = "Append content to end of existing file")]
pub append: bool,
#[arg(long, help = "Prepend content to beginning of existing file")]
pub prepend: bool,
#[arg(
long,
help = "Only write if current checksum matches (optimistic lock)"
)]
pub expect_checksum: Option<String>,
#[arg(
long,
value_enum,
default_value_t = crate::line_endings::LineEnding::Auto,
help = "Normalize line endings: lf, crlf, cr, auto (preserve original)"
)]
pub line_ending: crate::line_endings::LineEnding,
#[arg(long, help = "Show what would be done without writing")]
pub dry_run: bool,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum FuzzyMode {
Auto,
Off,
Aggressive,
}
#[derive(Args, Debug)]
pub struct EditArgs {
pub path: PathBuf,
#[arg(long, help = "Insert content from stdin after line N")]
pub after_line: Option<usize>,
#[arg(long, help = "Insert content from stdin before line N")]
pub before_line: Option<usize>,
#[arg(long, help = "Replace line range N:M with stdin content")]
pub range: Option<String>,
#[arg(long, help = "Delete line range N:M")]
pub delete_range: Option<String>,
#[arg(long, help = "Insert stdin content after first match of text")]
pub after_match: Option<String>,
#[arg(long, help = "Insert stdin content before first match of text")]
pub before_match: Option<String>,
#[arg(
long,
num_args = 2,
help = "Replace content between two markers with stdin"
)]
pub between: Option<Vec<String>>,
#[arg(long, action = clap::ArgAction::Append, help = "Exact text to find (repeatable)")]
pub old: Vec<String>,
#[arg(long, action = clap::ArgAction::Append, help = "Replacement text for --old (repeatable)")]
pub new: Vec<String>,
#[arg(
long,
value_enum,
default_value_t = FuzzyMode::Auto,
help = "Fuzzy match mode for --old/--new: auto, off, aggressive"
)]
pub fuzzy: FuzzyMode,
#[arg(long, help = "Read multiple edit operations as NDJSON from stdin")]
pub multi: bool,
#[arg(long, help = "Only edit if current checksum matches (optimistic lock)")]
pub expect_checksum: Option<String>,
#[arg(
long,
value_enum,
default_value_t = crate::line_endings::LineEnding::Auto,
help = "Normalize line endings: lf, crlf, cr, auto (preserve original)"
)]
pub line_ending: crate::line_endings::LineEnding,
#[arg(long, help = "Show what would be done without writing")]
pub dry_run: bool,
#[arg(long, help = "Preserve original mtime (default: update mtime to now)")]
pub preserve_timestamps: bool,
}
#[derive(Args, Debug)]
pub struct SearchArgs {
pub pattern: String,
#[arg(default_value = ".")]
pub paths: Vec<PathBuf>,
#[arg(short = 'e', long, help = "Treat pattern as regex (default)")]
pub regex: bool,
#[arg(short = 'F', long, help = "Treat pattern as fixed string")]
pub fixed: bool,
#[arg(short = 'w', long, help = "Match whole words only")]
pub word: bool,
#[arg(short = 'i', long, help = "Case-insensitive search")]
pub case_insensitive: bool,
#[arg(
short = 'S',
long,
help = "Smart case: insensitive if pattern is lowercase"
)]
pub smart_case: bool,
#[arg(
short = 'C',
long,
default_value_t = 0,
help = "Lines of context around matches"
)]
pub context: usize,
#[arg(short = 'g', long, action = clap::ArgAction::Append, help = "Include files matching glob")]
pub include: Vec<String>,
#[arg(long, action = clap::ArgAction::Append, help = "Exclude files matching glob")]
pub exclude: Vec<String>,
#[arg(short = 'c', long, help = "Only show match count per file")]
pub count: bool,
#[arg(short = 'l', long, help = "Only show filenames with matches")]
pub files: bool,
#[arg(short = 'm', long, help = "Maximum matches per file")]
pub max_count: Option<u64>,
#[arg(short = 'U', long, help = "Enable multi-line matching")]
pub multiline: bool,
#[arg(long, help = "Show lines that do NOT match")]
pub invert: bool,
#[arg(long, value_enum, help = "Sort results by criterion")]
pub sort: Option<SortBy>,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum SortBy {
Path,
Modified,
Created,
None,
}
#[derive(Args, Debug)]
pub struct ReplaceArgs {
pub pattern: String,
pub replacement: String,
#[arg(default_value = ".")]
pub paths: Vec<PathBuf>,
#[arg(long, help = "Treat pattern as regex")]
pub regex: bool,
#[arg(short = 'w', long, help = "Match whole words only")]
pub word: bool,
#[arg(
short = 'F',
long,
help = "Treat pattern as literal string (escape regex chars)"
)]
pub literal: bool,
#[arg(long, help = "Create backup before modifying")]
pub backup: bool,
#[arg(short = 'g', long, action = clap::ArgAction::Append, help = "Include files matching glob")]
pub include: Vec<String>,
#[arg(long, action = clap::ArgAction::Append, help = "Exclude files matching glob")]
pub exclude: Vec<String>,
#[arg(long, help = "Show diff preview without writing")]
pub preview: bool,
#[arg(short = 'n', long, help = "Maximum replacements per file")]
pub max_replacements: Option<usize>,
#[arg(
long,
help = "Only replace if current checksum matches (optimistic lock)"
)]
pub expect_checksum: Option<String>,
#[arg(long, help = "Show what would be done without writing")]
pub dry_run: bool,
#[arg(long, help = "Preserve original mtime (default: update mtime to now)")]
pub preserve_timestamps: bool,
}
#[derive(Args, Debug)]
pub struct ListArgs {
#[arg(default_value = ".")]
pub paths: Vec<PathBuf>,
#[arg(short = 'd', long, help = "Maximum directory depth")]
pub depth: Option<usize>,
#[arg(short = 'l', long, help = "Show size and modification time")]
pub long: bool,
#[arg(long, help = "Group file counts by extension")]
pub count_by_ext: bool,
#[arg(long, help = "Show all files including hidden")]
pub all: bool,
#[arg(short = 'g', long, action = clap::ArgAction::Append, help = "Include files matching glob")]
pub include: Vec<String>,
#[arg(long, action = clap::ArgAction::Append, help = "Exclude files matching glob")]
pub exclude: Vec<String>,
}
#[derive(Args, Debug)]
pub struct ExtractArgs {
pub fields: Vec<String>,
#[arg(
short = 'd',
long,
help = "Delimiter for text mode (default: whitespace)"
)]
pub delimiter: Option<String>,
#[arg(long, help = "Read input from stdin")]
pub stdin: bool,
}
#[derive(Args, Debug)]
pub struct CalcArgs {
pub expression: Option<String>,
#[arg(long, help = "Read expressions from stdin (one per line)")]
pub stdin: bool,
}
#[derive(Args, Debug)]
pub struct RegexArgs {
pub examples: Vec<String>,
#[arg(long, help = "Read examples from stdin (one per line)")]
pub stdin: bool,
#[arg(short = 'd', long, help = "Convert digits to \\d")]
pub digits: bool,
#[arg(short = 'w', long, help = "Convert words to \\w")]
pub words: bool,
#[arg(short = 's', long, help = "Convert whitespace to \\s")]
pub spaces: bool,
#[arg(short = 'r', long, help = "Detect repetitions")]
pub repetitions: bool,
#[arg(short = 'i', long, help = "Case-insensitive matching")]
pub case_insensitive: bool,
#[arg(long, help = "Remove anchors (^ and $)")]
pub no_anchors: bool,
}
#[derive(Args, Debug)]
pub struct TransformArgs {
#[arg(default_value = ".")]
pub paths: Vec<PathBuf>,
#[arg(short = 'p', long, required = true, help = "AST pattern to match")]
pub pattern: String,
#[arg(short = 'r', long, required = true, help = "Rewrite template")]
pub rewrite: String,
#[arg(
short = 'l',
long = "language",
required = true,
help = "Language (rust, js, ts, py, go, etc)"
)]
pub language: String,
#[arg(short = 'g', long, action = clap::ArgAction::Append, help = "Include files matching glob")]
pub include: Vec<String>,
#[arg(long, action = clap::ArgAction::Append, help = "Exclude files matching glob")]
pub exclude: Vec<String>,
#[arg(long, help = "Show diff preview without writing")]
pub dry_run: bool,
#[arg(long, help = "Create backup before modifying")]
pub backup: bool,
}
#[derive(Args, Debug)]
pub struct BatchArgs {
#[arg(long, help = "Show what would be done without executing")]
pub dry_run: bool,
#[arg(long, help = "Read manifest from file instead of stdin")]
pub file: Option<PathBuf>,
#[arg(
long,
help = "All-or-nothing: rollback all changes if any operation fails"
)]
pub transaction: bool,
#[arg(long, help = "Print JSON Schema for the batch input manifest")]
pub input_schema: bool,
}
#[derive(Args, Debug)]
pub struct BackupArgs {
#[arg(required = true)]
pub paths: Vec<PathBuf>,
#[arg(long, help = "Directory to store backup files")]
pub output_dir: Option<PathBuf>,
#[arg(long, default_value_t = 5, help = "Number of backup copies to keep")]
pub retention: u8,
#[arg(long, help = "Show what would be done without writing")]
pub dry_run: bool,
}
#[derive(Args, Debug)]
pub struct RollbackArgs {
pub path: PathBuf,
#[arg(long, help = "Timestamp of the backup to restore")]
pub timestamp: Option<String>,
#[arg(long, help = "Restore the most recent backup (default)")]
pub latest: bool,
#[arg(long, help = "Verify checksum after restoring")]
pub verify: bool,
#[arg(long, help = "Show what would be done without writing")]
pub dry_run: bool,
}
#[derive(Debug, Clone, Copy, ValueEnum, Default)]
pub enum PatchFormat {
#[default]
Auto,
Unified,
SearchReplace,
Full,
Markdown,
}
#[derive(Args, Debug)]
pub struct ApplyArgs {
pub file: PathBuf,
#[arg(long, value_enum, default_value_t = PatchFormat::Auto, help = "Patch format: auto, unified, search-replace, full, markdown")]
pub format: PatchFormat,
#[arg(long, help = "Create backup of target before patching")]
pub backup: bool,
#[arg(long, help = "Show what would be done without writing")]
pub dry_run: bool,
}