git_x/
new_branch.rs

1use std::process::Command;
2
3pub fn run(branch_name: String, from: Option<String>) {
4    // Validate branch name
5    if let Err(msg) = validate_branch_name(&branch_name) {
6        eprintln!("{}", format_error_message(msg));
7        return;
8    }
9
10    // Check if branch already exists
11    if branch_exists(&branch_name) {
12        eprintln!("{}", format_branch_exists_message(&branch_name));
13        return;
14    }
15
16    // Determine base branch
17    let base_branch = match from {
18        Some(ref branch) => {
19            if !branch_exists(branch) && !is_valid_ref(branch) {
20                eprintln!("{}", format_invalid_base_message(branch));
21                return;
22            }
23            branch.clone()
24        }
25        None => match get_current_branch() {
26            Ok(branch) => branch,
27            Err(msg) => {
28                eprintln!("{}", format_error_message(msg));
29                return;
30            }
31        },
32    };
33
34    println!(
35        "{}",
36        format_creating_branch_message(&branch_name, &base_branch)
37    );
38
39    // Create the new branch
40    if let Err(msg) = create_branch(&branch_name, &base_branch) {
41        eprintln!("{}", format_error_message(msg));
42        return;
43    }
44
45    // Switch to the new branch
46    if let Err(msg) = switch_to_branch(&branch_name) {
47        eprintln!("{}", format_error_message(msg));
48        return;
49    }
50
51    println!("{}", format_success_message(&branch_name));
52}
53
54// Helper function to validate branch name
55fn validate_branch_name(name: &str) -> Result<(), &'static str> {
56    if name.is_empty() {
57        return Err("Branch name cannot be empty");
58    }
59
60    if name.starts_with('-') {
61        return Err("Branch name cannot start with a dash");
62    }
63
64    if name.contains("..") {
65        return Err("Branch name cannot contain '..'");
66    }
67
68    if name.contains(' ') {
69        return Err("Branch name cannot contain spaces");
70    }
71
72    // Check for invalid characters
73    const INVALID_CHARS: &[char] = &['~', '^', ':', '?', '*', '[', '\\'];
74    if name.chars().any(|c| INVALID_CHARS.contains(&c)) {
75        return Err("Branch name contains invalid characters");
76    }
77
78    Ok(())
79}
80
81// Helper function to check if branch exists
82fn branch_exists(branch_name: &str) -> bool {
83    Command::new("git")
84        .args([
85            "show-ref",
86            "--verify",
87            "--quiet",
88            &format!("refs/heads/{branch_name}"),
89        ])
90        .status()
91        .map(|status| status.success())
92        .unwrap_or(false)
93}
94
95// Helper function to check if ref is valid
96fn is_valid_ref(ref_name: &str) -> bool {
97    Command::new("git")
98        .args(["rev-parse", "--verify", "--quiet", ref_name])
99        .status()
100        .map(|status| status.success())
101        .unwrap_or(false)
102}
103
104// Helper function to get current branch
105fn get_current_branch() -> Result<String, &'static str> {
106    let output = Command::new("git")
107        .args(["rev-parse", "--abbrev-ref", "HEAD"])
108        .output()
109        .map_err(|_| "Failed to get current branch")?;
110
111    if !output.status.success() {
112        return Err("Not in a git repository");
113    }
114
115    Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
116}
117
118// Helper function to create branch
119fn create_branch(branch_name: &str, base_branch: &str) -> Result<(), &'static str> {
120    let status = Command::new("git")
121        .args(["branch", branch_name, base_branch])
122        .status()
123        .map_err(|_| "Failed to execute git branch command")?;
124
125    if !status.success() {
126        return Err("Failed to create branch");
127    }
128
129    Ok(())
130}
131
132// Helper function to switch to branch
133fn switch_to_branch(branch_name: &str) -> Result<(), &'static str> {
134    let status = Command::new("git")
135        .args(["switch", branch_name])
136        .status()
137        .map_err(|_| "Failed to execute git switch command")?;
138
139    if !status.success() {
140        return Err("Failed to switch to new branch");
141    }
142
143    Ok(())
144}
145
146// Helper function to get branch validation error messages
147pub fn get_validation_rules() -> &'static [&'static str] {
148    &[
149        "Cannot be empty",
150        "Cannot start with a dash",
151        "Cannot contain '..'",
152        "Cannot contain spaces",
153        "Cannot contain ~^:?*[\\",
154    ]
155}
156
157// Helper function to format error message
158pub fn format_error_message(msg: &str) -> String {
159    format!("❌ {msg}")
160}
161
162// Helper function to format branch exists message
163pub fn format_branch_exists_message(branch_name: &str) -> String {
164    format!("❌ Branch '{branch_name}' already exists")
165}
166
167// Helper function to format invalid base message
168pub fn format_invalid_base_message(base_branch: &str) -> String {
169    format!("❌ Base branch or ref '{base_branch}' does not exist")
170}
171
172// Helper function to format creating branch message
173pub fn format_creating_branch_message(branch_name: &str, base_branch: &str) -> String {
174    format!("🌿 Creating branch '{branch_name}' from '{base_branch}'...")
175}
176
177// Helper function to format success message
178pub fn format_success_message(branch_name: &str) -> String {
179    format!("✅ Created and switched to branch '{branch_name}'")
180}
181
182// Helper function to get git branch creation args
183pub fn get_git_branch_args() -> [&'static str; 2] {
184    ["branch", "-"]
185}
186
187// Helper function to get git switch args
188pub fn get_git_switch_args() -> [&'static str; 2] {
189    ["switch", "-"]
190}