git_workflow/cli.rs
1//! CLI argument parsing with clap
2
3use clap::{Parser, Subcommand};
4
5#[derive(Parser)]
6#[command(name = "gw")]
7#[command(about = "Git workflow CLI - type-safe worktree-aware git operations")]
8#[command(version)]
9pub struct Cli {
10 #[command(subcommand)]
11 pub command: Commands,
12
13 /// Show verbose output (git commands being run)
14 #[arg(short, long, global = true)]
15 pub verbose: bool,
16}
17
18#[derive(Subcommand)]
19pub enum Commands {
20 /// Switch to home branch and sync with origin/main
21 Home,
22
23 /// Create new branch from origin/main
24 New {
25 /// Name of the branch to create (e.g., feature/add-login)
26 branch: Option<String>,
27 },
28
29 /// Delete merged branch and return to home
30 Cleanup {
31 /// Branch to delete (defaults to current branch if not on home)
32 branch: Option<String>,
33 },
34
35 /// Show current repository state
36 Status,
37
38 /// Pause current work: WIP commit and return to home branch
39 Pause {
40 /// Optional message describing why work is paused
41 message: Option<String>,
42 },
43
44 /// Abandon current changes and return to home branch
45 Abandon,
46
47 /// Undo the last commit (soft reset HEAD~1)
48 Undo,
49
50 /// Sync current branch after base PR is merged (update base to main, rebase, force push)
51 Sync,
52
53 /// Open the PR for the current branch in the browser
54 Open,
55
56 /// Watch the current branch's PR until merged or closed, then clean up
57 Await {
58 /// Also open the PR in the browser before watching
59 #[arg(long)]
60 open: bool,
61
62 /// Skip waiting for CI checks
63 #[arg(long = "no-wait")]
64 no_wait: bool,
65
66 /// Do not clean up the branch after the PR is merged
67 #[arg(long = "no-cleanup")]
68 no_cleanup: bool,
69
70 /// Seconds between merge-status polls
71 #[arg(long, default_value_t = 30)]
72 interval: u64,
73 },
74
75 /// Manage worktrees
76 Worktree {
77 #[command(subcommand)]
78 command: WorktreeCommands,
79 },
80}
81
82#[derive(Subcommand)]
83pub enum WorktreeCommands {
84 /// Manage a pre-warmed worktree pool
85 Pool {
86 #[command(subcommand)]
87 command: PoolCommands,
88 },
89}
90
91#[derive(Subcommand)]
92pub enum PoolCommands {
93 /// Pre-warm the pool with ready-to-use worktrees
94 Warm {
95 /// Target number of available worktrees in the pool
96 count: usize,
97 },
98
99 /// Acquire a worktree from the pool (prints path to stdout)
100 Acquire,
101
102 /// Show pool status
103 Status,
104
105 /// Release acquired worktree(s) back to the pool
106 Release {
107 /// Name of the worktree to release (defaults to all acquired)
108 name: Option<String>,
109 },
110
111 /// Remove all worktrees and clean up the pool
112 Drain {
113 /// Force drain even if worktrees are acquired
114 #[arg(long)]
115 force: bool,
116 },
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn await_defaults() {
125 let cli = Cli::try_parse_from(["gw", "await"]).unwrap();
126 match cli.command {
127 Commands::Await {
128 open,
129 no_wait,
130 no_cleanup,
131 interval,
132 } => {
133 assert!(!open);
134 assert!(!no_wait);
135 assert!(!no_cleanup);
136 assert_eq!(interval, 30);
137 }
138 _ => panic!("expected Await command"),
139 }
140 }
141
142 #[test]
143 fn await_flags() {
144 let cli = Cli::try_parse_from([
145 "gw",
146 "await",
147 "--open",
148 "--no-wait",
149 "--no-cleanup",
150 "--interval",
151 "5",
152 ])
153 .unwrap();
154 match cli.command {
155 Commands::Await {
156 open,
157 no_wait,
158 no_cleanup,
159 interval,
160 } => {
161 assert!(open);
162 assert!(no_wait);
163 assert!(no_cleanup);
164 assert_eq!(interval, 5);
165 }
166 _ => panic!("expected Await command"),
167 }
168 }
169}