cuddle-please-misc 0.1.0

A release-please inspired release manager tool, built on top of cuddle, but also useful standalone, cuddle-please supports, your ci of choice, as well as gitea, github
Documentation
use semver::{Prerelease, Version};

use super::conventional_parse::VersionIncrement;

pub trait NextVersion {
    fn next<I>(&self, commits: I) -> Self
    where
        I: IntoIterator,
        I::Item: AsRef<str>;
}

impl NextVersion for Version {
    fn next<I>(&self, commits: I) -> Self
    where
        I: IntoIterator,
        I::Item: AsRef<str>,
    {
        let increment = VersionIncrement::from(self, commits);

        match increment {
            Some(increment) => match increment {
                VersionIncrement::Major => Self {
                    major: self.major + 1,
                    minor: 0,
                    patch: 0,
                    pre: Prerelease::EMPTY,
                    ..self.clone()
                },
                VersionIncrement::Minor => Self {
                    minor: self.minor + 1,
                    patch: 0,
                    pre: Prerelease::EMPTY,
                    ..self.clone()
                },
                VersionIncrement::Patch => Self {
                    patch: self.patch + 1,
                    pre: Prerelease::EMPTY,
                    ..self.clone()
                },
                VersionIncrement::Prerelease => Self {
                    pre: {
                        let release = &self.pre;
                        let release_version = match release.rsplit_once('.') {
                            Some((tag, version)) => match version.parse::<usize>() {
                                Ok(version) => format!("{tag}.{}", version + 1),
                                Err(_) => format!("{tag}.1"),
                            },
                            None => format!("{release}.1"),
                        };
                        Prerelease::new(&release_version).expect("prerelease is not valid semver")
                    },
                    ..self.clone()
                },
            },
            None => self.clone(),
        }
    }
}

#[cfg(test)]
mod test {
    use semver::Version;
    use tracing_test::traced_test;

    use crate::versioning::next_version::NextVersion;

    #[test]
    #[traced_test]
    fn is_no_bump() {
        let version = Version::parse("0.0.0-prerelease").unwrap();
        let commits: Vec<&str> = vec![];

        let actual = version.next(commits);

        assert_eq!("0.0.0-prerelease", actual.to_string())
    }

    #[test]
    #[traced_test]
    fn is_prerelease_initial() {
        let version = Version::parse("0.0.0-prerelease").unwrap();
        let commits: Vec<&str> = vec!["feat: something"];

        let actual = version.next(commits);

        assert_eq!("0.0.0-prerelease.1", actual.to_string())
    }

    #[test]
    #[traced_test]
    fn is_prerelease_invalid() {
        let version = Version::parse("0.0.0-prerelease.invalid").unwrap();
        let commits: Vec<&str> = vec!["feat: something"];

        let actual = version.next(commits);

        assert_eq!("0.0.0-prerelease.1", actual.to_string())
    }

    #[test]
    #[traced_test]
    fn is_prerelease_next() {
        let version = Version::parse("0.0.0-prerelease.1").unwrap();
        let commits: Vec<&str> = vec!["feat: something"];

        let actual = version.next(commits);

        assert_eq!("0.0.0-prerelease.2", actual.to_string())
    }

    #[test]
    #[traced_test]
    fn is_patch() {
        let version = Version::parse("0.0.0").unwrap();
        let commits: Vec<&str> = vec!["fix: something"];

        let actual = version.next(commits);

        assert_eq!("0.0.1", actual.to_string())
    }

    #[test]
    #[traced_test]
    fn is_minor() {
        let version = Version::parse("0.1.0").unwrap();
        let commits: Vec<&str> = vec!["feat: something"];

        let actual = version.next(commits);

        assert_eq!("0.2.0", actual.to_string())
    }
    #[test]
    #[traced_test]
    fn is_minor_clears_patch() {
        let version = Version::parse("0.1.1").unwrap();
        let commits: Vec<&str> = vec!["feat: something"];

        let actual = version.next(commits);

        assert_eq!("0.2.0", actual.to_string())
    }
    #[test]
    #[traced_test]
    fn is_major() {
        let version = Version::parse("0.0.0").unwrap();
        let commits: Vec<&str> = vec![
            "feat: something
            
            BREAKING CHANGE: something",
        ];

        let actual = version.next(commits);

        assert_eq!("1.0.0", actual.to_string())
    }
    #[test]
    #[traced_test]
    fn is_major_clears_minor_patch() {
        let version = Version::parse("1.2.3").unwrap();
        let commits: Vec<&str> = vec![
            "feat: something
            
            BREAKING CHANGE: something",
        ];

        let actual = version.next(commits);

        assert_eq!("2.0.0", actual.to_string())
    }
}