Skip to main content

cargo_tag/
cli.rs

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    /// Bump crate's version and create a Git tag
24    Tag(TagArgs),
25}
26
27#[derive(clap::Args, Debug)]
28pub struct TagArgs {
29    #[command(subcommand)]
30    pub command: Command,
31
32    /// Prefix string for Git tags
33    #[arg(short, long)]
34    pub prefix: Option<String>,
35
36    /// Skip creating a Git commit
37    #[arg(long)]
38    pub no_commit: bool,
39
40    /// Skip creating a Git tag
41    #[arg(long)]
42    pub no_tag: bool,
43
44    /// Get name and email from environment variables CARGO_TAG_NAME and CARGO_TAG_EMAIL.
45    /// They must be set beforehand.
46    #[arg(long)]
47    pub env: bool,
48
49    /// Generate tag without writing files or creating git commits/tags
50    #[arg(long)]
51    pub dry_run: bool,
52}
53
54#[derive(Clone, Subcommand, Debug)]
55pub enum Command {
56    /// Print current package version
57    Current,
58    /// Bumps crate's minor version and create a git tag
59    Minor,
60    /// Bumps crate's major version and create a git tag
61    Major,
62    /// Bumps crate's patch version and create a git tag
63    Patch,
64    /// Sets the provided prerelease string and create a git tag
65    #[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}