git_editor/
args.rs

1use clap::Parser;
2
3#[derive(Parser)]
4#[command(author, version, about)]
5pub struct Args {
6    #[arg(
7        short = 'r',
8        long = "repo-path",
9        help = "Path or URI to the repository"
10    )]
11    pub repo_path: Option<String>,
12
13    #[arg(long, help = "Email associated with the commits")]
14    pub email: Option<String>,
15
16    #[arg(short = 'n', long = "name", help = "Name associated with the commits")]
17    pub name: Option<String>,
18
19    #[arg(
20        short = 'b',
21        long = "begin",
22        help = "Start date for the commits in YYYY-MM-DD format"
23    )]
24    pub start: Option<String>,
25
26    #[arg(
27        short = 'e',
28        long = "end",
29        help = "End date for the commits in YYYY-MM-DD format"
30    )]
31    pub end: Option<String>,
32
33    #[arg(
34        short = 's',
35        long = "show-history",
36        help = "Show updated commit history after rewriting"
37    )]
38    pub show_history: bool,
39
40    #[arg(
41        short = 'p',
42        long = "pick-specific-commits",
43        help = "Pick specific commits to rewrite. Provide a comma-separated list of commit hashes."
44    )]
45    pub pic_specific_commits: bool,
46
47    #[arg(
48        short = 'x',
49        long = "range",
50        help = "Edit a range of commits (e.g., --range to interactively select range)"
51    )]
52    pub range: bool,
53}
54
55impl Args {
56    pub fn is_help_request(&self) -> bool {
57        !self.show_history
58            && !self.pic_specific_commits
59            && !self.range
60            && self.email.is_none()
61            && self.name.is_none()
62            && self.start.is_none()
63            && self.end.is_none()
64    }
65
66    pub fn ensure_all_args_present(&mut self) {
67        use crate::utils::prompt::prompt_for_missing_arg;
68
69        if self.repo_path.is_none() {
70            self.repo_path = Some(String::from("./"));
71        }
72
73        // Skip prompting for email, name, start, and end if using show_history or pic_specific_commits
74        if self.show_history || self.pic_specific_commits {
75            return;
76        }
77
78        // Range mode will prompt for its own parameters interactively
79        if self.range {
80            return;
81        }
82
83        if self.email.is_none() {
84            self.email = Some(prompt_for_missing_arg("email"));
85        }
86
87        if self.name.is_none() {
88            self.name = Some(prompt_for_missing_arg("name"));
89        }
90
91        if self.start.is_none() {
92            self.start = Some(prompt_for_missing_arg("start date (YYYY-MM-DD HH:MM:SS)"));
93        }
94
95        if self.end.is_none() {
96            self.end = Some(prompt_for_missing_arg("end date (YYYY-MM-DD HH:MM:SS)"));
97        }
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn test_args_default_values() {
107        let args = Args {
108            repo_path: None,
109            email: None,
110            name: None,
111            start: None,
112            end: None,
113            show_history: false,
114            pic_specific_commits: false,
115            range: false,
116        };
117
118        assert_eq!(args.repo_path, None);
119        assert_eq!(args.email, None);
120        assert_eq!(args.name, None);
121        assert_eq!(args.start, None);
122        assert_eq!(args.end, None);
123        assert!(!args.show_history);
124        assert!(!args.pic_specific_commits);
125        assert!(!args.range);
126    }
127
128    #[test]
129    fn test_args_with_show_history() {
130        let args = Args {
131            repo_path: Some("/test/repo".to_string()),
132            email: None,
133            name: None,
134            start: None,
135            end: None,
136            show_history: true,
137            pic_specific_commits: false,
138            range: false,
139        };
140
141        assert_eq!(args.repo_path, Some("/test/repo".to_string()));
142        assert!(args.show_history);
143        assert!(!args.pic_specific_commits);
144    }
145
146    #[test]
147    fn test_args_with_pick_specific_commits() {
148        let args = Args {
149            repo_path: Some("/test/repo".to_string()),
150            email: None,
151            name: None,
152            start: None,
153            end: None,
154            show_history: false,
155            pic_specific_commits: true,
156            range: false,
157        };
158
159        assert_eq!(args.repo_path, Some("/test/repo".to_string()));
160        assert!(!args.show_history);
161        assert!(args.pic_specific_commits);
162    }
163
164    #[test]
165    fn test_args_full_rewrite() {
166        let args = Args {
167            repo_path: Some("/test/repo".to_string()),
168            email: Some("test@example.com".to_string()),
169            name: Some("Test User".to_string()),
170            start: Some("2023-01-01 00:00:00".to_string()),
171            end: Some("2023-01-02 00:00:00".to_string()),
172            show_history: false,
173            pic_specific_commits: false,
174            range: false,
175        };
176
177        assert_eq!(args.repo_path, Some("/test/repo".to_string()));
178        assert_eq!(args.email, Some("test@example.com".to_string()));
179        assert_eq!(args.name, Some("Test User".to_string()));
180        assert_eq!(args.start, Some("2023-01-01 00:00:00".to_string()));
181        assert_eq!(args.end, Some("2023-01-02 00:00:00".to_string()));
182    }
183
184    #[test]
185    fn test_args_with_range() {
186        let args = Args {
187            repo_path: Some("/test/repo".to_string()),
188            email: None,
189            name: None,
190            start: None,
191            end: None,
192            show_history: false,
193            pic_specific_commits: false,
194            range: true,
195        };
196
197        assert_eq!(args.repo_path, Some("/test/repo".to_string()));
198        assert!(!args.show_history);
199        assert!(!args.pic_specific_commits);
200        assert!(args.range);
201    }
202
203    #[test]
204    fn test_is_help_request() {
205        // Default args should trigger help
206        let args = Args {
207            repo_path: None,
208            email: None,
209            name: None,
210            start: None,
211            end: None,
212            show_history: false,
213            pic_specific_commits: false,
214            range: false,
215        };
216        assert!(args.is_help_request());
217
218        // Args with repo_path only should still trigger help
219        let args = Args {
220            repo_path: Some("/test/repo".to_string()),
221            email: None,
222            name: None,
223            start: None,
224            end: None,
225            show_history: false,
226            pic_specific_commits: false,
227            range: false,
228        };
229        assert!(args.is_help_request());
230
231        // Args with show_history should NOT trigger help
232        let args = Args {
233            repo_path: None,
234            email: None,
235            name: None,
236            start: None,
237            end: None,
238            show_history: true,
239            pic_specific_commits: false,
240            range: false,
241        };
242        assert!(!args.is_help_request());
243
244        // Args with email should NOT trigger help
245        let args = Args {
246            repo_path: None,
247            email: Some("test@example.com".to_string()),
248            name: None,
249            start: None,
250            end: None,
251            show_history: false,
252            pic_specific_commits: false,
253            range: false,
254        };
255        assert!(!args.is_help_request());
256
257        // Args with range should NOT trigger help
258        let args = Args {
259            repo_path: None,
260            email: None,
261            name: None,
262            start: None,
263            end: None,
264            show_history: false,
265            pic_specific_commits: false,
266            range: true,
267        };
268        assert!(!args.is_help_request());
269
270        // Args with pic_specific_commits should NOT trigger help
271        let args = Args {
272            repo_path: None,
273            email: None,
274            name: None,
275            start: None,
276            end: None,
277            show_history: false,
278            pic_specific_commits: true,
279            range: false,
280        };
281        assert!(!args.is_help_request());
282    }
283}