use semver::{Prerelease, Version}; use super::conventional_parse::VersionIncrement; pub trait NextVersion { fn next(&self, commits: I) -> Self where I: IntoIterator, I::Item: AsRef; } impl NextVersion for Version { fn next(&self, commits: I) -> Self where I: IntoIterator, I::Item: AsRef, { 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::() { 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()) } }