Skip to main content

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}