Skip to main content

git_worktree_manager/
messages.rs

1//! Standardized error and informational messages for git-worktree-manager.
2//!
3
4pub fn worktree_not_found(branch: &str) -> String {
5    format!(
6        "No worktree found for branch '{}'. Use 'gw list' to see available worktrees.",
7        branch
8    )
9}
10
11pub fn target_not_found(target: &str) -> String {
12    format!(
13        "No worktree matches '{}' (tried name, branch, path). Use 'gw list' to see available worktrees.",
14        target
15    )
16}
17
18pub fn branch_not_found(branch: &str) -> String {
19    format!("Branch '{}' not found", branch)
20}
21
22pub fn invalid_branch_name(error_msg: &str) -> String {
23    format!(
24        "Invalid branch name: {}\nHint: Use alphanumeric characters, hyphens, and slashes. \
25         Avoid special characters like emojis, backslashes, or control characters.",
26        error_msg
27    )
28}
29
30pub fn cannot_determine_branch() -> String {
31    "Cannot determine current branch".to_string()
32}
33
34pub fn cannot_determine_base_branch() -> String {
35    "Cannot determine base branch. Specify with --base or checkout a branch first.".to_string()
36}
37
38pub fn missing_metadata(branch: &str) -> String {
39    format!(
40        "Missing metadata for branch '{}'. Was this worktree created with 'gw new'?",
41        branch
42    )
43}
44
45pub fn worktree_dir_not_found(path: &str) -> String {
46    format!("Worktree directory does not exist: {}", path)
47}
48
49pub fn cannot_delete_main_worktree() -> String {
50    "Cannot delete main repository worktree".to_string()
51}
52
53pub fn detached_head_warning() -> String {
54    "Worktree is detached or branch not found. Specify branch with --branch or skip with --force."
55        .to_string()
56}
57
58// ---------------------------------------------------------------------------
59// Status / progress messages (used in styled println! calls)
60// ---------------------------------------------------------------------------
61
62pub fn deleting_local_branch(branch: &str) -> String {
63    format!("Deleting local branch: {}", branch)
64}
65
66pub fn deleting_remote_branch(branch: &str) -> String {
67    format!("Deleting remote branch: origin/{}", branch)
68}
69
70pub fn removing_worktree(path: &std::path::Path) -> String {
71    format!("Removing worktree: {}", path.display())
72}
73
74pub fn starting_ai_tool_foreground(tool_name: &str) -> String {
75    format!("Starting {} (Ctrl+C to exit)...", tool_name)
76}
77
78pub fn starting_ai_tool_in(tool_name: &str) -> String {
79    format!("Starting {} in:", tool_name)
80}
81
82pub fn resuming_ai_tool_in(tool_name: &str) -> String {
83    format!("Resuming {} in:", tool_name)
84}
85
86pub fn switched_to_worktree(path: &std::path::Path) -> String {
87    format!("Switched to worktree: {}", path.display())
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_worktree_not_found() {
96        let msg = worktree_not_found("feature-x");
97        assert!(msg.contains("feature-x"));
98        assert!(msg.contains("gw list"));
99        assert!(msg.contains("No worktree found"));
100    }
101
102    #[test]
103    fn test_target_not_found() {
104        let msg = target_not_found("my-target");
105        assert!(msg.contains("my-target"));
106        assert!(msg.contains("name, branch, path"));
107        assert!(msg.contains("gw list"));
108    }
109
110    #[test]
111    fn test_branch_not_found() {
112        let msg = branch_not_found("my-branch");
113        assert!(msg.contains("my-branch"));
114        assert!(msg.contains("not found"));
115    }
116
117    #[test]
118    fn test_invalid_branch_name() {
119        let msg = invalid_branch_name("contains spaces");
120        assert!(msg.contains("contains spaces"));
121        assert!(msg.contains("Invalid branch name"));
122        assert!(msg.contains("Hint"));
123        assert!(msg.contains("alphanumeric"));
124    }
125
126    #[test]
127    fn test_cannot_determine_branch() {
128        let msg = cannot_determine_branch();
129        assert!(msg.contains("Cannot determine current branch"));
130    }
131
132    #[test]
133    fn test_cannot_determine_base_branch() {
134        let msg = cannot_determine_base_branch();
135        assert!(msg.contains("Cannot determine base branch"));
136        assert!(msg.contains("--base"));
137    }
138
139    #[test]
140    fn test_missing_metadata() {
141        let msg = missing_metadata("feat-login");
142        assert!(msg.contains("feat-login"));
143        assert!(msg.contains("Missing metadata"));
144        assert!(msg.contains("gw new"));
145    }
146
147    #[test]
148    fn test_worktree_dir_not_found() {
149        let msg = worktree_dir_not_found("/tmp/worktree");
150        assert!(msg.contains("/tmp/worktree"));
151        assert!(msg.contains("does not exist"));
152    }
153
154    #[test]
155    fn test_cannot_delete_main_worktree() {
156        let msg = cannot_delete_main_worktree();
157        assert!(msg.contains("Cannot delete main repository worktree"));
158    }
159
160    #[test]
161    fn test_detached_head_warning() {
162        let msg = detached_head_warning();
163        assert!(msg.contains("detached"));
164        assert!(msg.contains("--branch"));
165        assert!(msg.contains("--force"));
166    }
167
168    #[test]
169    fn test_deleting_local_branch() {
170        let msg = deleting_local_branch("feat-x");
171        assert!(msg.contains("Deleting local branch: feat-x"));
172    }
173
174    #[test]
175    fn test_deleting_remote_branch() {
176        let msg = deleting_remote_branch("feat-x");
177        assert!(msg.contains("origin/feat-x"));
178    }
179
180    #[test]
181    fn test_removing_worktree() {
182        let msg = removing_worktree(std::path::Path::new("/tmp/wt"));
183        assert!(msg.contains("Removing worktree:"));
184        assert!(msg.contains("/tmp/wt"));
185    }
186
187    #[test]
188    fn test_starting_ai_tool_foreground() {
189        let msg = starting_ai_tool_foreground("claude");
190        assert!(msg.contains("Starting claude"));
191        assert!(msg.contains("Ctrl+C"));
192    }
193
194    #[test]
195    fn test_starting_ai_tool_in() {
196        let msg = starting_ai_tool_in("claude");
197        assert_eq!(msg, "Starting claude in:");
198    }
199
200    #[test]
201    fn test_resuming_ai_tool_in() {
202        let msg = resuming_ai_tool_in("claude");
203        assert_eq!(msg, "Resuming claude in:");
204    }
205
206    #[test]
207    fn test_switched_to_worktree() {
208        let msg = switched_to_worktree(std::path::Path::new("/tmp/wt"));
209        assert!(msg.contains("Switched to worktree:"));
210        assert!(msg.contains("/tmp/wt"));
211    }
212}