1use anyhow::Result;
2use clap::{Parser, Subcommand};
3
4use crate::cargo_toml::CargoToml;
5use crate::git::Git;
6use crate::version::Version;
7
8const ABOUT: &str = r#"Cargo plugin to bump crate's versions and Git tag them
9for release.
10
11"cargo tag" helps to automate the process of bumping versions
12similar to how "npm version" does.
13
14When bumping versions with "cargo tag", the
15Cargo.toml and Cargo.lock files are updated with the new version, then a Git
16commit and a Git tag are both created."#;
17
18#[derive(Parser)]
19#[command(bin_name = "cargo")]
20#[command(next_line_help = true)]
21#[command(name = "cargo", author, version, about, long_about = Some(ABOUT))]
22pub enum Cli {
23 Tag(TagArgs),
25}
26
27#[derive(clap::Args, Debug)]
28pub struct TagArgs {
29 #[command(subcommand)]
30 pub command: Command,
31
32 #[arg(short, long)]
34 pub prefix: Option<String>,
35
36 #[arg(long)]
38 pub no_commit: bool,
39
40 #[arg(long)]
42 pub no_tag: bool,
43
44 #[arg(long)]
47 pub env: bool,
48
49 #[arg(long)]
51 pub dry_run: bool,
52}
53
54#[derive(Clone, Subcommand, Debug)]
55pub enum Command {
56 Current,
58 Minor,
60 Major,
62 Patch,
64 #[clap(name = "prerelease")]
66 PreRelease { prerelease: String },
67}
68
69impl Command {
70 pub fn exec(&self, args: &TagArgs) -> Result<()> {
71 let cargo_toml = CargoToml::open()?;
72 let mut version = Version::from(&cargo_toml.manifest.package().version);
73
74 match *self {
75 Command::Current => {
76 println!("{}", cargo_toml.manifest.package().version);
77 return Ok(());
78 }
79 Command::Major | Command::Minor | Command::Patch => {
80 match self {
81 Command::Major => version.bump_major(),
82 Command::Minor => version.bump_minor(),
83 Command::Patch => version.bump_patch(),
84 _ => unreachable!(),
85 };
86
87 if !args.dry_run {
88 cargo_toml.write_version(&version)?;
89 cargo_toml.run_cargo_fetch()?;
90 }
91 }
92 Command::PreRelease { ref prerelease } => {
93 version.set_prerelease(prerelease)?;
94 if !args.dry_run {
95 cargo_toml.write_version(&version)?;
96 cargo_toml.run_cargo_fetch()?;
97 }
98 }
99 }
100
101 let prefix = args.prefix.clone().unwrap_or_default();
102 let version_str = prefix + &version.to_string();
103
104 if !args.dry_run {
105 let repository = if args.env {
106 Git::from_env("main")?
107 } else {
108 Git::from_git_config("main")?
109 };
110
111 if !args.no_commit {
112 repository.commit(&format!("chore: bump version to {}", version_str))?;
113 }
114
115 if !args.no_tag {
116 repository.tag(
117 &version_str,
118 &format!("chore: bump version to {}", version_str),
119 )?;
120 }
121 }
122
123 println!("{version_str}");
124
125 Ok(())
126 }
127}