use std::env;
use std::fs;
use std::process::{Command, exit};
fn run_command(cmd: &str, args: &[&str]) {
println!("Running: {} {}", cmd, args.join(" "));
let status = Command::new(cmd)
.args(args)
.status()
.unwrap_or_else(|err| {
eprintln!("Failed to execute command '{}': {}", cmd, err);
exit(1);
});
if !status.success() {
eprintln!("Error: Command '{}' failed with exit code {:?}", cmd, status.code());
exit(status.code().unwrap_or(1));
}
}
fn main() {
println!("=== Running pre-bump checks ===");
run_command("cargo", &["fmt", "--all", "--", "--check"]);
run_command("cargo", &["clippy", "--all-targets", "--all-features", "--", "-D", "warnings"]);
run_command("cargo", &["test", "--all-targets", "--all-features"]);
println!("=== Pre-bump checks passed successfully! ===\n");
let cargo_toml_path = "Cargo.toml";
let content = fs::read_to_string(cargo_toml_path).unwrap_or_else(|err| {
eprintln!("Error reading {}: {}", cargo_toml_path, err);
exit(1);
});
let mut in_package = false;
let mut current_version = None;
let mut version_line_index = None;
let lines: Vec<&str> = content.lines().collect();
for (i, line) in lines.iter().enumerate() {
let trimmed = line.trim();
if trimmed == "[package]" {
in_package = true;
continue;
}
if in_package && trimmed.starts_with('[') {
in_package = false;
}
if in_package && trimmed.starts_with("version") {
if let Some(start) = line.find('"') {
if let Some(end) = line[start + 1..].find('"') {
let version = &line[start + 1..start + 1 + end];
current_version = Some(version.to_string());
version_line_index = Some(i);
break;
}
}
}
}
let current_version = current_version.unwrap_or_else(|| {
eprintln!("Error: Could not find version in [package] section of Cargo.toml");
exit(1);
});
let version_line_idx = version_line_index.unwrap();
println!("Current package version: {}", current_version);
let args: Vec<String> = env::args().collect();
let target_version = if args.len() > 1 && !args[1].trim().is_empty() {
let val = args[1].trim().to_string();
println!("Bumping to user-specified version: {}", val);
val
} else {
let parts: Vec<&str> = current_version.split('.').collect();
if parts.len() != 3 {
eprintln!("Error: Current version '{}' is not in standard X.Y.Z semver format.", current_version);
exit(1);
}
let patch: u32 = parts[2].parse().unwrap_or_else(|_| {
eprintln!("Error: Patch version part '{}' in '{}' is not an integer.", parts[2], current_version);
exit(1);
});
let new_patch = patch + 1;
let val = format!("{}.{}.{}", parts[0], parts[1], new_patch);
println!("Auto-bumping patch version: {} -> {}", current_version, val);
val
};
let mut new_lines = lines.clone();
let old_line = lines[version_line_idx];
let new_line = if let Some(start) = old_line.find('"') {
if let Some(end) = old_line[start + 1..].find('"') {
let prefix = &old_line[..start + 1];
let suffix = &old_line[start + 1 + end..];
format!("{}{}{}", prefix, target_version, suffix)
} else {
format!("version = \"{}\"", target_version)
}
} else {
format!("version = \"{}\"", target_version)
};
new_lines[version_line_idx] = &new_line;
let new_content = new_lines.join("\n") + "\n";
fs::write(cargo_toml_path, new_content).unwrap_or_else(|err| {
eprintln!("Error writing {}: {}", cargo_toml_path, err);
exit(1);
});
println!("Updated Cargo.toml successfully.");
println!("Staging version changes...");
run_command("git", &["add", "Cargo.toml"]);
let commit_msg = format!("chore: bump version to {}", target_version);
println!("Committing changes (excluding git hooks with --no-verify)...");
run_command("git", &["commit", "-m", &commit_msg, "--no-verify"]);
let tag_name = format!("v{}", target_version);
println!("Creating git tag: {}...", tag_name);
run_command("git", &["tag", "-a", &tag_name, "-m", &format!("Release {}", tag_name)]);
println!("\n=======================================================");
println!("SUCCESS: Version bumped, committed, and tagged locally!");
println!("New Version: {}", target_version);
println!("Git Tag: {}", tag_name);
println!("=======================================================");
println!("To push the changes and the tag to GitHub, run:");
println!(" git push origin && git push origin {} --no-verify", tag_name);
println!("=======================================================");
}