feat: add semver

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2023-07-30 14:03:24 +02:00
parent a63c6ce3be
commit 120865f72c
Signed by: kjuulh
GPG Key ID: 9AA7BC13CE474394
8 changed files with 170 additions and 5 deletions

7
Cargo.lock generated
View File

@ -215,6 +215,7 @@ dependencies = [
"clap", "clap",
"dotenv", "dotenv",
"reqwest", "reqwest",
"semver",
"serde", "serde",
"serde_yaml", "serde_yaml",
"tokio", "tokio",
@ -928,6 +929,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "semver"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.177" version = "1.0.177"

View File

@ -15,6 +15,7 @@ url = {version = "*"}
serde_yaml = {version = "*"} serde_yaml = {version = "*"}
serde = {version = "*", features = ["derive"]} serde = {version = "*", features = ["derive"]}
semver = "1.0.18"
reqwest = {version = "*"} reqwest = {version = "*"}

View File

@ -14,6 +14,7 @@ serde_yaml.workspace = true
serde.workspace = true serde.workspace = true
reqwest = {workspace = true, features = ["blocking", "json"]} reqwest = {workspace = true, features = ["blocking", "json"]}
url.workspace = true url.workspace = true
semver.workspace = true
[dev-dependencies] [dev-dependencies]
tracing-test = {workspace = true, features = ["no-env-filter"]} tracing-test = {workspace = true, features = ["no-env-filter"]}

View File

@ -216,7 +216,7 @@ pub struct CommitDetails {
pub message: String, pub message: String,
} }
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct Tag { pub struct Tag {
pub id: String, pub id: String,
pub message: String, pub message: String,
@ -224,11 +224,11 @@ pub struct Tag {
pub commit: TagCommit, pub commit: TagCommit,
} }
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct TagCommit { pub struct TagCommit {
created: String, pub created: String,
pub sha: String, pub sha: String,
url: String, pub url: String,
} }
#[cfg(test)] #[cfg(test)]

View File

@ -3,3 +3,4 @@ pub mod environment;
pub mod git_client; pub mod git_client;
pub mod gitea_client; pub mod gitea_client;
pub mod ui; pub mod ui;
pub mod versioning;

View File

@ -3,6 +3,7 @@ pub mod environment;
pub mod git_client; pub mod git_client;
pub mod gitea_client; pub mod gitea_client;
pub mod ui; pub mod ui;
pub mod versioning;
use command::Command; use command::Command;

View File

@ -0,0 +1 @@
pub mod semver;

View File

@ -0,0 +1,153 @@
use std::cmp::Reverse;
use crate::gitea_client::{Commit, Tag};
use semver::Version;
pub fn get_most_significant_version<'a>(commits: Vec<&'a Tag>) -> Option<&'a Tag> {
let mut versions: Vec<(&'a Tag, Version)> = commits
.into_iter()
.filter_map(|c| {
if let Some(version) = c.name.trim_start_matches("v").parse::<Version>().ok() {
Some((c, version))
} else {
None
}
})
.collect();
versions.sort_unstable_by_key(|(_, version)| Reverse(version.clone()));
versions.first().map(|(tag, _)| *tag)
}
#[cfg(test)]
mod test {
use tracing_test::traced_test;
use crate::{
gitea_client::{Tag, TagCommit},
versioning::semver::get_most_significant_version,
};
fn create_tag(version: impl Into<String>) -> Tag {
let version = version.into();
Tag {
id: "some-id".into(),
message: version.clone(),
name: version,
commit: TagCommit {
created: "date".into(),
sha: "sha".into(),
url: "url".into(),
},
}
}
#[test]
#[traced_test]
fn gets_most_significant_version() {
let most_significant = create_tag("3.1.1");
let tags = vec![
create_tag("1.0.1"),
create_tag("1.2.1"),
most_significant.clone(),
create_tag("0.0.1"),
create_tag("0.0.2"),
];
let actual = get_most_significant_version(tags.iter().collect()).unwrap();
assert_eq!(&most_significant, actual)
}
#[test]
#[traced_test]
fn gets_most_significant_version_patch() {
let most_significant = create_tag("0.0.8");
let tags = vec![
create_tag("0.0.1"),
create_tag("0.0.7"),
create_tag("0.0.2"),
most_significant.clone(),
create_tag("0.0.0"),
];
let actual = get_most_significant_version(tags.iter().collect()).unwrap();
assert_eq!(&most_significant, actual)
}
#[test]
#[traced_test]
fn gets_most_significant_version_minor() {
let most_significant = create_tag("0.8.0");
let tags = vec![
create_tag("0.1.1"),
create_tag("0.2.7"),
create_tag("0.7.2"),
most_significant.clone(),
create_tag("0.3.0"),
];
let actual = get_most_significant_version(tags.iter().collect()).unwrap();
assert_eq!(&most_significant, actual)
}
#[test]
#[traced_test]
fn gets_most_significant_version_major() {
let most_significant = create_tag("7.8.0");
let tags = vec![
create_tag("6.1.1"),
create_tag("1.2.7"),
create_tag("2.7.2"),
most_significant.clone(),
create_tag("3.3.0"),
];
let actual = get_most_significant_version(tags.iter().collect()).unwrap();
assert_eq!(&most_significant, actual)
}
#[test]
#[traced_test]
fn ignored_invalid_tags() {
let tags = vec![
create_tag("something-3.3.0"),
create_tag("bla bla bla"),
create_tag("main"),
create_tag("master"),
create_tag("develop"),
];
let actual = get_most_significant_version(tags.iter().collect()).is_none();
assert!(actual)
}
#[test]
#[traced_test]
fn mix_v_prefix() {
let most_significant = create_tag("v7.8.0");
let tags = vec![
create_tag("6.1.1"),
create_tag("v1.2.7"),
create_tag("2.7.2"),
most_significant.clone(),
create_tag("v3.3.0"),
];
let actual = get_most_significant_version(tags.iter().collect()).unwrap();
assert_eq!(&most_significant, actual)
}
#[test]
#[traced_test]
fn mix_v_prefix_2() {
let most_significant = create_tag("7.8.0");
let tags = vec![
create_tag("6.1.1"),
create_tag("v1.2.7"),
create_tag("2.7.2"),
most_significant.clone(),
create_tag("v3.3.0"),
];
let actual = get_most_significant_version(tags.iter().collect()).unwrap();
assert_eq!(&most_significant, actual)
}
}