Skip to main content

atomwrite/
cli_args.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Subcommand argument structs and value enums for clap.
4
5use std::path::PathBuf;
6
7use clap::{Args, ValueEnum};
8
9/// Arguments for shell completion script generation.
10#[derive(Args, Debug)]
11pub struct CompletionsArgs {
12    /// Target shell for completion scripts.
13    #[arg(value_enum)]
14    pub shell: ShellType,
15
16    /// Install completion script to XDG data directory.
17    #[arg(
18        long,
19        help = "Install completion script to XDG data directory (Bash: ~/.local/share/bash-completion/completions/atomwrite)"
20    )]
21    pub install: bool,
22}
23
24/// Supported shell types for completion generation.
25#[derive(Debug, Clone, Copy, ValueEnum)]
26pub enum ShellType {
27    /// Bash shell.
28    Bash,
29    /// Zsh shell.
30    Zsh,
31    /// Fish shell.
32    Fish,
33    /// `PowerShell`.
34    #[value(name = "powershell")]
35    PowerShell,
36    /// Elvish shell.
37    Elvish,
38}
39
40/// Arguments for the hash subcommand.
41#[derive(Args, Debug)]
42pub struct HashArgs {
43    /// File paths to hash.
44    #[arg(required = true)]
45    pub paths: Vec<PathBuf>,
46
47    /// Expected BLAKE3 hash for verification.
48    #[arg(long, help = "Verify file checksum against expected BLAKE3 hash")]
49    pub verify: Option<String>,
50
51    /// Hash content from stdin.
52    #[arg(long, help = "Hash content from stdin instead of files")]
53    pub stdin: bool,
54
55    /// Recurse into directories.
56    #[arg(short, long, help = "Recurse into directories")]
57    pub recursive: bool,
58}
59
60/// Arguments for the delete subcommand.
61#[derive(Args, Debug)]
62pub struct DeleteArgs {
63    /// File paths to delete.
64    #[arg(required = true)]
65    pub paths: Vec<PathBuf>,
66
67    /// Create backup before deleting.
68    #[arg(long, help = "Create backup before deleting")]
69    pub backup: bool,
70
71    /// Number of backups to retain.
72    #[arg(long, default_value_t = 5, help = "Number of backups to retain")]
73    pub retention: u8,
74
75    /// Recurse into directories.
76    #[arg(short, long, help = "Recurse into directories")]
77    pub recursive: bool,
78
79    /// Glob patterns for file inclusion.
80    #[arg(short = 'g', long, action = clap::ArgAction::Append, help = "Include files matching glob")]
81    pub include: Vec<String>,
82
83    /// Glob patterns for file exclusion.
84    #[arg(long, action = clap::ArgAction::Append, help = "Exclude files matching glob")]
85    pub exclude: Vec<String>,
86
87    /// Preview without deleting.
88    #[arg(long, help = "Show what would be done without deleting")]
89    pub dry_run: bool,
90
91    /// Skip confirmation prompt.
92    #[arg(short = 'y', long, help = "Skip confirmation")]
93    pub yes: bool,
94}
95
96/// Arguments for the count subcommand.
97#[derive(Args, Debug)]
98pub struct CountArgs {
99    /// Paths to count within.
100    #[arg(default_value = ".")]
101    pub paths: Vec<PathBuf>,
102
103    /// Group counts by file extension.
104    #[arg(long, help = "Group counts by file extension")]
105    pub by_extension: bool,
106
107    /// Sort results by file size.
108    #[arg(long, help = "Sort by file size (top N)")]
109    pub by_size: bool,
110
111    /// Number of top results to show.
112    #[arg(long, default_value_t = 10, help = "Number of top results")]
113    pub top: usize,
114
115    /// Glob patterns for file inclusion.
116    #[arg(short = 'g', long, action = clap::ArgAction::Append, help = "Include files matching glob")]
117    pub include: Vec<String>,
118
119    /// Glob patterns for file exclusion.
120    #[arg(long, action = clap::ArgAction::Append, help = "Exclude files matching glob")]
121    pub exclude: Vec<String>,
122}
123
124/// Arguments for the diff subcommand.
125#[derive(Args, Debug)]
126pub struct DiffArgs {
127    /// First file to compare.
128    pub file_a: PathBuf,
129    /// Second file to compare.
130    pub file_b: PathBuf,
131
132    /// Output unified diff format.
133    #[arg(long, help = "Output unified diff format")]
134    pub unified: bool,
135
136    /// Show only summary statistics.
137    #[arg(long, help = "Only show summary statistics")]
138    pub stat: bool,
139
140    /// Lines of context in unified diff.
141    #[arg(
142        short = 'C',
143        long,
144        default_value_t = 3,
145        help = "Lines of context in unified diff"
146    )]
147    pub context: usize,
148
149    /// Diff algorithm to use.
150    #[arg(long, value_enum, default_value_t = DiffAlgorithm::Patience, help = "Diff algorithm")]
151    pub algorithm: DiffAlgorithm,
152}
153
154/// Available diff algorithms for file comparison.
155#[derive(Debug, Clone, Copy, ValueEnum)]
156pub enum DiffAlgorithm {
157    /// Myers linear-space diff algorithm.
158    Myers,
159    /// Patience diff algorithm.
160    Patience,
161    /// Longest common subsequence algorithm.
162    Lcs,
163}
164
165/// Arguments for the move subcommand.
166#[derive(Args, Debug)]
167pub struct MoveArgs {
168    /// Source file path.
169    pub source: PathBuf,
170    /// Destination file path.
171    pub target: PathBuf,
172
173    /// Create backup of destination if it exists.
174    #[arg(long, help = "Create backup of destination if it exists")]
175    pub backup: bool,
176
177    /// Number of backups to retain.
178    #[arg(long, default_value_t = 5, help = "Number of backups to retain")]
179    pub retention: u8,
180
181    /// Overwrite destination if it exists.
182    #[arg(short, long, help = "Overwrite destination if it exists")]
183    pub force: bool,
184
185    /// Preview without moving.
186    #[arg(long, help = "Show what would be done without moving")]
187    pub dry_run: bool,
188}
189
190/// Arguments for the copy subcommand.
191#[derive(Args, Debug)]
192pub struct CopyArgs {
193    /// Source file path.
194    pub source: PathBuf,
195    /// Destination file path.
196    pub target: PathBuf,
197
198    /// Create backup of destination if it exists.
199    #[arg(long, help = "Create backup of destination if it exists")]
200    pub backup: bool,
201
202    /// Overwrite destination if it exists.
203    #[arg(short, long, help = "Overwrite destination if it exists")]
204    pub force: bool,
205
206    /// Copy directories recursively.
207    #[arg(short, long, help = "Copy directories recursively")]
208    pub recursive: bool,
209
210    /// Preserve timestamps and permissions.
211    #[arg(long, help = "Preserve timestamps and permissions")]
212    pub preserve: bool,
213
214    /// Preview without copying.
215    #[arg(long, help = "Show what would be done without copying")]
216    pub dry_run: bool,
217}
218
219/// Arguments for the read subcommand.
220#[derive(Args, Debug)]
221pub struct ReadArgs {
222    /// File path to read.
223    pub path: PathBuf,
224
225    /// Line range to read (1-based, e.g. "1:50").
226    #[arg(long, help = "Line range to read (1-based, e.g. 1:50)")]
227    pub lines: Option<String>,
228
229    /// Single line number to read.
230    #[arg(long, help = "Single line number with optional context")]
231    pub line: Option<usize>,
232
233    /// Lines of context around --line.
234    #[arg(
235        short = 'C',
236        long,
237        default_value_t = 0,
238        help = "Lines of context around --line"
239    )]
240    pub context: usize,
241
242    /// Read first N lines.
243    #[arg(long, help = "Read first N lines")]
244    pub head: Option<usize>,
245
246    /// Read last N lines.
247    #[arg(long, help = "Read last N lines")]
248    pub tail: Option<usize>,
249
250    /// Return only metadata without content.
251    #[arg(long, help = "Return only metadata (no content)")]
252    pub stat: bool,
253
254    /// Output format selection.
255    #[arg(long, value_enum, default_value_t = OutputFormat::Ndjson, help = "Output format")]
256    pub format: OutputFormat,
257
258    /// Expected BLAKE3 hash for verification.
259    #[arg(long, help = "Verify file checksum against expected BLAKE3 hash")]
260    pub verify_checksum: Option<String>,
261
262    /// Filter lines matching this regex (substring of file content).
263    #[arg(long, help = "Filter returned lines to those matching this regex")]
264    pub grep: Option<String>,
265}
266
267/// Output format for the read subcommand.
268#[derive(Debug, Clone, Copy, ValueEnum)]
269pub enum OutputFormat {
270    /// Structured NDJSON output.
271    Ndjson,
272    /// Raw file content output.
273    Raw,
274}
275
276/// Arguments for the write subcommand.
277#[derive(Args, Debug)]
278pub struct WriteArgs {
279    /// Target file path.
280    pub target: PathBuf,
281
282    /// Create backup before overwriting.
283    #[arg(long, help = "Create backup before overwriting")]
284    pub backup: bool,
285
286    /// Number of backups to retain.
287    #[arg(long, default_value_t = 5, help = "Number of backups to retain")]
288    pub retention: u8,
289
290    /// Maximum input size in bytes.
291    #[arg(long, help = "Maximum input size in bytes")]
292    pub max_size: Option<u64>,
293
294    /// Append content to end of file.
295    #[arg(long, help = "Append content to end of existing file")]
296    pub append: bool,
297
298    /// Prepend content to beginning of file.
299    #[arg(long, help = "Prepend content to beginning of existing file")]
300    pub prepend: bool,
301
302    /// Expected checksum for optimistic locking.
303    #[arg(
304        long,
305        help = "Only write if current checksum matches (optimistic lock)"
306    )]
307    pub expect_checksum: Option<String>,
308
309    /// Line ending normalization mode.
310    #[arg(
311        long,
312        value_enum,
313        default_value_t = crate::line_endings::LineEnding::Auto,
314        help = "Normalize line endings: lf, crlf, cr, auto (preserve original)"
315    )]
316    pub line_ending: crate::line_endings::LineEnding,
317
318    /// Preview without writing.
319    #[arg(long, help = "Show what would be done without writing")]
320    pub dry_run: bool,
321}
322
323/// Fuzzy matching behavior for --old/--new edit mode.
324#[derive(Debug, Clone, Copy, ValueEnum)]
325pub enum FuzzyMode {
326    /// Try exact match first, then fuzzy strategies automatically.
327    Auto,
328    /// Exact match only, no fuzzy fallback.
329    Off,
330    /// Try all fuzzy strategies including low-confidence block anchor.
331    Aggressive,
332}
333
334/// Arguments for the edit subcommand.
335#[derive(Args, Debug)]
336pub struct EditArgs {
337    /// File path to edit.
338    pub path: PathBuf,
339
340    /// Insert stdin content after line N.
341    #[arg(long, help = "Insert content from stdin after line N")]
342    pub after_line: Option<usize>,
343
344    /// Insert stdin content before line N.
345    #[arg(long, help = "Insert content from stdin before line N")]
346    pub before_line: Option<usize>,
347
348    /// Replace line range N:M with stdin content.
349    #[arg(long, help = "Replace line range N:M with stdin content")]
350    pub range: Option<String>,
351
352    /// Delete line range N:M.
353    #[arg(long, help = "Delete line range N:M")]
354    pub delete_range: Option<String>,
355
356    /// Insert stdin content after first match of text.
357    #[arg(long, help = "Insert stdin content after first match of text")]
358    pub after_match: Option<String>,
359
360    /// Insert stdin content before first match of text.
361    #[arg(long, help = "Insert stdin content before first match of text")]
362    pub before_match: Option<String>,
363
364    /// Two markers delimiting content to replace with stdin.
365    #[arg(
366        long,
367        num_args = 2,
368        help = "Replace content between two markers with stdin"
369    )]
370    pub between: Option<Vec<String>>,
371
372    /// Exact text to find (repeatable for multiple replacements).
373    #[arg(long, action = clap::ArgAction::Append, help = "Exact text to find (repeatable)")]
374    pub old: Vec<String>,
375
376    /// Replacement text for --old (repeatable, must match --old count).
377    #[arg(long, action = clap::ArgAction::Append, help = "Replacement text for --old (repeatable)")]
378    pub new: Vec<String>,
379
380    /// Fuzzy matching mode for --old/--new.
381    #[arg(
382        long,
383        value_enum,
384        default_value_t = FuzzyMode::Auto,
385        help = "Fuzzy match mode for --old/--new: auto, off, aggressive"
386    )]
387    pub fuzzy: FuzzyMode,
388
389    /// Read multiple edit operations as NDJSON from stdin.
390    #[arg(long, help = "Read multiple edit operations as NDJSON from stdin")]
391    pub multi: bool,
392
393    /// Expected checksum for optimistic locking.
394    #[arg(long, help = "Only edit if current checksum matches (optimistic lock)")]
395    pub expect_checksum: Option<String>,
396
397    /// Line ending normalization mode.
398    #[arg(
399        long,
400        value_enum,
401        default_value_t = crate::line_endings::LineEnding::Auto,
402        help = "Normalize line endings: lf, crlf, cr, auto (preserve original)"
403    )]
404    pub line_ending: crate::line_endings::LineEnding,
405
406    /// Preview without writing.
407    #[arg(long, help = "Show what would be done without writing")]
408    pub dry_run: bool,
409
410    /// Preserve original modification time (mtime) of the file.
411    /// Default is false: mtime is updated to reflect the edit.
412    /// Set true for backup workflows, version control snapshots, or
413    /// reproducible builds that depend on stable timestamps.
414    /// Note: setting true may break build systems that use mtime to
415    /// detect source changes (cargo, make, cmake, gradle).
416    #[arg(long, help = "Preserve original mtime (default: update mtime to now)")]
417    pub preserve_timestamps: bool,
418}
419
420/// Arguments for the search subcommand.
421#[derive(Args, Debug)]
422pub struct SearchArgs {
423    /// Search pattern (regex by default).
424    pub pattern: String,
425
426    /// Paths to search within.
427    #[arg(default_value = ".")]
428    pub paths: Vec<PathBuf>,
429
430    /// Treat pattern as regex.
431    #[arg(short = 'e', long, help = "Treat pattern as regex (default)")]
432    pub regex: bool,
433
434    /// Treat pattern as fixed string.
435    #[arg(short = 'F', long, help = "Treat pattern as fixed string")]
436    pub fixed: bool,
437
438    /// Match whole words only.
439    #[arg(short = 'w', long, help = "Match whole words only")]
440    pub word: bool,
441
442    /// Case-insensitive search.
443    #[arg(short = 'i', long, help = "Case-insensitive search")]
444    pub case_insensitive: bool,
445
446    /// Smart case: insensitive when pattern is lowercase.
447    #[arg(
448        short = 'S',
449        long,
450        help = "Smart case: insensitive if pattern is lowercase"
451    )]
452    pub smart_case: bool,
453
454    /// Lines of context around matches.
455    #[arg(
456        short = 'C',
457        long,
458        default_value_t = 0,
459        help = "Lines of context around matches"
460    )]
461    pub context: usize,
462
463    /// Glob patterns for file inclusion.
464    #[arg(short = 'g', long, action = clap::ArgAction::Append, help = "Include files matching glob")]
465    pub include: Vec<String>,
466
467    /// Glob patterns for file exclusion.
468    #[arg(long, action = clap::ArgAction::Append, help = "Exclude files matching glob")]
469    pub exclude: Vec<String>,
470
471    /// Show only match count per file.
472    #[arg(short = 'c', long, help = "Only show match count per file")]
473    pub count: bool,
474
475    /// Show only filenames with matches.
476    #[arg(short = 'l', long, help = "Only show filenames with matches")]
477    pub files: bool,
478
479    /// Maximum matches per file.
480    #[arg(short = 'm', long, help = "Maximum matches per file")]
481    pub max_count: Option<u64>,
482
483    /// Enable multi-line matching.
484    #[arg(short = 'U', long, help = "Enable multi-line matching")]
485    pub multiline: bool,
486
487    /// Show non-matching lines.
488    #[arg(long, help = "Show lines that do NOT match")]
489    pub invert: bool,
490
491    /// Sort results by criterion.
492    #[arg(long, value_enum, help = "Sort results by criterion")]
493    pub sort: Option<SortBy>,
494}
495
496/// Sort criterion for search results.
497#[derive(Debug, Clone, Copy, ValueEnum)]
498pub enum SortBy {
499    /// Sort by file path.
500    Path,
501    /// Sort by modification time.
502    Modified,
503    /// Sort by creation time.
504    Created,
505    /// No sorting.
506    None,
507}
508
509/// Arguments for the replace subcommand.
510#[derive(Args, Debug)]
511pub struct ReplaceArgs {
512    /// Pattern to search for.
513    pub pattern: String,
514    /// Replacement text.
515    pub replacement: String,
516
517    /// Paths to search within.
518    #[arg(default_value = ".")]
519    pub paths: Vec<PathBuf>,
520
521    /// Treat pattern as regex.
522    #[arg(long, help = "Treat pattern as regex")]
523    pub regex: bool,
524
525    /// Match whole words only.
526    #[arg(short = 'w', long, help = "Match whole words only")]
527    pub word: bool,
528
529    /// Treat pattern as literal string.
530    #[arg(
531        short = 'F',
532        long,
533        help = "Treat pattern as literal string (escape regex chars)"
534    )]
535    pub literal: bool,
536
537    /// Create backup before modifying.
538    #[arg(long, help = "Create backup before modifying")]
539    pub backup: bool,
540
541    /// Glob patterns for file inclusion.
542    #[arg(short = 'g', long, action = clap::ArgAction::Append, help = "Include files matching glob")]
543    pub include: Vec<String>,
544
545    /// Glob patterns for file exclusion.
546    #[arg(long, action = clap::ArgAction::Append, help = "Exclude files matching glob")]
547    pub exclude: Vec<String>,
548
549    /// Show diff preview without writing.
550    #[arg(long, help = "Show diff preview without writing")]
551    pub preview: bool,
552
553    /// Maximum replacements per file.
554    #[arg(short = 'n', long, help = "Maximum replacements per file")]
555    pub max_replacements: Option<usize>,
556
557    /// Expected checksum for optimistic locking.
558    #[arg(
559        long,
560        help = "Only replace if current checksum matches (optimistic lock)"
561    )]
562    pub expect_checksum: Option<String>,
563
564    /// Preview without writing.
565    #[arg(long, help = "Show what would be done without writing")]
566    pub dry_run: bool,
567
568    /// Preserve original modification time (mtime) of replaced files.
569    /// Default is false: mtime is updated to reflect the change.
570    /// Set true for backup workflows, version control snapshots, or
571    /// reproducible builds that depend on stable timestamps.
572    /// Note: setting true may break build systems that use mtime to
573    /// detect source changes (cargo, make, cmake, gradle).
574    #[arg(long, help = "Preserve original mtime (default: update mtime to now)")]
575    pub preserve_timestamps: bool,
576}
577
578/// Arguments for the list subcommand.
579#[derive(Args, Debug)]
580pub struct ListArgs {
581    /// Paths to list.
582    #[arg(default_value = ".")]
583    pub paths: Vec<PathBuf>,
584
585    /// Maximum directory depth.
586    #[arg(short = 'd', long, help = "Maximum directory depth")]
587    pub depth: Option<usize>,
588
589    /// Show size and modification time.
590    #[arg(short = 'l', long, help = "Show size and modification time")]
591    pub long: bool,
592
593    /// Group file counts by extension.
594    #[arg(long, help = "Group file counts by extension")]
595    pub count_by_ext: bool,
596
597    /// Show all files including hidden.
598    #[arg(long, help = "Show all files including hidden")]
599    pub all: bool,
600
601    /// Glob patterns for file inclusion.
602    #[arg(short = 'g', long, action = clap::ArgAction::Append, help = "Include files matching glob")]
603    pub include: Vec<String>,
604
605    /// Glob patterns for file exclusion.
606    #[arg(long, action = clap::ArgAction::Append, help = "Exclude files matching glob")]
607    pub exclude: Vec<String>,
608}
609
610/// Arguments for the extract subcommand.
611#[derive(Args, Debug)]
612pub struct ExtractArgs {
613    /// Field names or indices to extract.
614    pub fields: Vec<String>,
615
616    /// Delimiter for text mode (default: whitespace).
617    #[arg(
618        short = 'd',
619        long,
620        help = "Delimiter for text mode (default: whitespace)"
621    )]
622    pub delimiter: Option<String>,
623
624    /// Read input from stdin.
625    #[arg(long, help = "Read input from stdin")]
626    pub stdin: bool,
627}
628
629/// Arguments for the calc subcommand.
630#[derive(Args, Debug)]
631pub struct CalcArgs {
632    /// Math expression to evaluate.
633    pub expression: Option<String>,
634
635    /// Read expressions from stdin.
636    #[arg(long, help = "Read expressions from stdin (one per line)")]
637    pub stdin: bool,
638}
639
640/// Arguments for the regex subcommand.
641#[derive(Args, Debug)]
642pub struct RegexArgs {
643    /// Example strings for regex generation.
644    pub examples: Vec<String>,
645
646    /// Read examples from stdin.
647    #[arg(long, help = "Read examples from stdin (one per line)")]
648    pub stdin: bool,
649
650    /// Convert digits to \\d.
651    #[arg(short = 'd', long, help = "Convert digits to \\d")]
652    pub digits: bool,
653
654    /// Convert words to \\w.
655    #[arg(short = 'w', long, help = "Convert words to \\w")]
656    pub words: bool,
657
658    /// Convert whitespace to \\s.
659    #[arg(short = 's', long, help = "Convert whitespace to \\s")]
660    pub spaces: bool,
661
662    /// Detect repetitions.
663    #[arg(short = 'r', long, help = "Detect repetitions")]
664    pub repetitions: bool,
665
666    /// Case-insensitive matching.
667    #[arg(short = 'i', long, help = "Case-insensitive matching")]
668    pub case_insensitive: bool,
669
670    /// Remove anchors (^ and $).
671    #[arg(long, help = "Remove anchors (^ and $)")]
672    pub no_anchors: bool,
673}
674
675/// Arguments for the transform subcommand.
676#[derive(Args, Debug)]
677pub struct TransformArgs {
678    /// Paths to search for transforms.
679    #[arg(default_value = ".")]
680    pub paths: Vec<PathBuf>,
681
682    /// AST pattern to match.
683    #[arg(short = 'p', long, required = true, help = "AST pattern to match")]
684    pub pattern: String,
685
686    /// Rewrite template for matched patterns.
687    #[arg(short = 'r', long, required = true, help = "Rewrite template")]
688    pub rewrite: String,
689
690    /// Source language for AST parsing.
691    #[arg(
692        short = 'l',
693        long = "language",
694        required = true,
695        help = "Language (rust, js, ts, py, go, etc)"
696    )]
697    pub language: String,
698
699    /// Glob patterns for file inclusion.
700    #[arg(short = 'g', long, action = clap::ArgAction::Append, help = "Include files matching glob")]
701    pub include: Vec<String>,
702
703    /// Glob patterns for file exclusion.
704    #[arg(long, action = clap::ArgAction::Append, help = "Exclude files matching glob")]
705    pub exclude: Vec<String>,
706
707    /// Preview without writing.
708    #[arg(long, help = "Show diff preview without writing")]
709    pub dry_run: bool,
710
711    /// Create backup before modifying.
712    #[arg(long, help = "Create backup before modifying")]
713    pub backup: bool,
714}
715
716/// Arguments for the batch subcommand.
717#[derive(Args, Debug)]
718pub struct BatchArgs {
719    /// Preview without executing.
720    #[arg(long, help = "Show what would be done without executing")]
721    pub dry_run: bool,
722
723    /// Manifest file path (default: stdin).
724    #[arg(long, help = "Read manifest from file instead of stdin")]
725    pub file: Option<PathBuf>,
726
727    /// Execute all operations as a transaction (all-or-nothing).
728    #[arg(
729        long,
730        help = "All-or-nothing: rollback all changes if any operation fails"
731    )]
732    pub transaction: bool,
733
734    /// Emit JSON Schema for the NDJSON input manifest format.
735    #[arg(long, help = "Print JSON Schema for the batch input manifest")]
736    pub input_schema: bool,
737}
738
739/// Arguments for the backup subcommand.
740#[derive(Args, Debug)]
741pub struct BackupArgs {
742    /// File paths to back up.
743    #[arg(required = true)]
744    pub paths: Vec<PathBuf>,
745
746    /// Directory to store backups (default: same as source).
747    #[arg(long, help = "Directory to store backup files")]
748    pub output_dir: Option<PathBuf>,
749
750    /// Maximum number of backups to retain per file.
751    #[arg(long, default_value_t = 5, help = "Number of backup copies to keep")]
752    pub retention: u8,
753
754    /// Preview without creating backups.
755    #[arg(long, help = "Show what would be done without writing")]
756    pub dry_run: bool,
757}
758
759/// Arguments for the rollback subcommand.
760#[derive(Args, Debug)]
761pub struct RollbackArgs {
762    /// File path to restore from backup.
763    pub path: PathBuf,
764
765    /// Restore a specific backup by timestamp (`YYYYMMDD_HHMMSS`).
766    #[arg(long, help = "Timestamp of the backup to restore")]
767    pub timestamp: Option<String>,
768
769    /// Restore the most recent backup.
770    #[arg(long, help = "Restore the most recent backup (default)")]
771    pub latest: bool,
772
773    /// Verify BLAKE3 checksum after restore.
774    #[arg(long, help = "Verify checksum after restoring")]
775    pub verify: bool,
776
777    /// Preview without restoring.
778    #[arg(long, help = "Show what would be done without writing")]
779    pub dry_run: bool,
780}
781
782/// Patch format for the apply subcommand.
783#[derive(Debug, Clone, Copy, ValueEnum, Default)]
784pub enum PatchFormat {
785    /// Auto-detect format from content.
786    #[default]
787    Auto,
788    /// Standard unified diff (--- +++ @@ markers).
789    Unified,
790    /// SEARCH/REPLACE block format (<<<<<<< SEARCH markers).
791    SearchReplace,
792    /// Full file replacement.
793    Full,
794    /// Markdown-fenced diff (`` ```diff `` blocks).
795    Markdown,
796}
797
798/// Arguments for the apply subcommand.
799#[derive(Args, Debug)]
800pub struct ApplyArgs {
801    /// Target file to apply the patch to.
802    pub file: PathBuf,
803
804    /// Patch format (default: auto-detect).
805    #[arg(long, value_enum, default_value_t = PatchFormat::Auto, help = "Patch format: auto, unified, search-replace, full, markdown")]
806    pub format: PatchFormat,
807
808    /// Create backup before applying patch.
809    #[arg(long, help = "Create backup of target before patching")]
810    pub backup: bool,
811
812    /// Preview without writing.
813    #[arg(long, help = "Show what would be done without writing")]
814    pub dry_run: bool,
815}