From 4af8c3d4c779cfb552f18d2fdda2ef3289183281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sun, 18 Aug 2024 17:34:30 +0100 Subject: [PATCH] Add vcs data --- Cargo.toml | 1 + src/upstream.rs | 343 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 344 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index ed5c70a..763dde4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ python-debian = [ "dep:pyo3",] serde = [ "dep:serde",] [dependencies] +chrono = { version = "0.4.38", default-features = false, features = ["alloc"] } lazy-regex = ">=2" [dependencies.pyo3] diff --git a/src/upstream.rs b/src/upstream.rs index 8096683..7e7775a 100644 --- a/src/upstream.rs +++ b/src/upstream.rs @@ -30,6 +30,193 @@ pub fn add_dfsg_suffix(upstream_version: &str, old_upstream_version: Option<&str upstream_version.to_string() + style.as_str() } +#[derive(Debug, Clone, PartialEq)] +pub enum VcsSnapshot { + Git { + date: Option, + sha: Option, + snapshot: Option, + }, + Bzr { + revno: String, + }, + Svn { + revno: usize, + }, +} + +impl VcsSnapshot { + fn to_suffix(&self) -> String { + match self { + VcsSnapshot::Git { + date, + sha, + snapshot, + } => { + let decoded_gitid = sha.as_ref().map(|sha| &sha[..std::cmp::min(sha.len(), 7)]); + let gitdate = date.map(|d| d.format("%Y%m%d").to_string()); + if let (Some(decoded_gitid), Some(snapshot), Some(gitdate)) = + (decoded_gitid, snapshot, gitdate.as_ref()) + { + format!("git{}.{}.{}", gitdate, snapshot, decoded_gitid) + } else if let (Some(decoded_gitid), Some(gitdate)) = + (decoded_gitid, gitdate.as_ref()) + { + format!("git{}.{}", gitdate, decoded_gitid) + } else if let Some(decoded_gitid) = decoded_gitid { + format!("git{}", decoded_gitid) + } else if let Some(gitdate) = gitdate { + format!("git{}", gitdate) + } else { + "git".to_string() + } + } + VcsSnapshot::Bzr { revno } => { + format!("bzr{}", revno) + } + VcsSnapshot::Svn { revno } => { + format!("svn{}", revno) + } + } + } +} + +pub enum Direction { + Before, + After, +} + +impl From<&str> for Direction { + fn from(s: &str) -> Self { + match s { + "~" => Direction::Before, + "+" => Direction::After, + _ => panic!("Invalid direction"), + } + } +} + +impl From for &str { + fn from(d: Direction) -> Self { + match d { + Direction::Before => "~", + Direction::After => "+", + } + } +} + +pub fn get_revision(version_string: &str) -> (&str, Option<(Direction, VcsSnapshot)>) { + if let Some((_, b, s, r)) = + lazy_regex::regex_captures!(r"^(.*)([\+~])bzr(\d+)$", version_string) + { + ( + b, + Some(( + s.into(), + VcsSnapshot::Bzr { + revno: r.to_string(), + }, + )), + ) + } else if let Some((_, b, s, d, i)) = + lazy_regex::regex_captures!(r"^(.*)([\+~-])git(\d{8})\.([a-f0-9]{7})$", version_string) + { + ( + b, + Some(( + s.into(), + VcsSnapshot::Git { + date: chrono::NaiveDate::parse_from_str(d, "%Y%m%d").ok(), + sha: Some(i.to_string()), + snapshot: None, + }, + )), + ) + } else if let Some((_, b, s, d, r, i)) = lazy_regex::regex_captures!( + r"^(.*)([\+~-])git(\d{8})\.(\d+)\.([a-f0-9]{7})$", + version_string + ) { + ( + b, + Some(( + s.into(), + VcsSnapshot::Git { + date: chrono::NaiveDate::parse_from_str(d, "%Y%m%d").ok(), + sha: Some(i.to_string()), + snapshot: r.parse().ok(), + }, + )), + ) + } else if let Some((_, b, s, r)) = + lazy_regex::regex_captures!(r"^(.*)([\+~-])svn(\d+)$", version_string) + { + ( + b, + Some(( + s.into(), + VcsSnapshot::Svn { + revno: r.parse().unwrap(), + }, + )), + ) + } else { + (version_string, None) + } +} + +/// Update the revision in a upstream version string. +/// +/// # Arguments +/// * `version_string` - Original version string +/// * `sep` - Separator to use when adding snapshot +/// * `vcs_snapshot` - VCS snapshot information +pub fn upstream_version_add_revision( + version_string: &str, + mut vcs_snapshot: VcsSnapshot, + sep: Option, +) -> String { + let plain_version = strip_dfsg_suffix(version_string).unwrap_or(version_string); + + let (base_version, current_sep, current_vcs) = match get_revision(plain_version) { + (base_version, Some((sep, current_vcs))) => (base_version, Some(sep), Some(current_vcs)), + (base_version, None) => (base_version, None, None), + }; + + let sep = sep.or(current_sep).unwrap_or(Direction::After); + + if let ( + VcsSnapshot::Git { + date, + sha, + snapshot, + }, + Some(VcsSnapshot::Git { + date: c_date, + sha: c_sha, + snapshot: c_snapshot, + }), + ) = (&mut vcs_snapshot, current_vcs.as_ref()) + { + if snapshot.is_none() { + *snapshot = if date.as_ref() == c_date.as_ref() && sha.as_ref() != c_sha.as_ref() { + c_snapshot.map(|s| s + 1) + } else { + Some(1) + }; + } + if c_date.is_none() { + *date = None; + } + if c_sha.is_none() { + *sha = None; + } + } + + let sep: &str = sep.into(); + + format!("{}{}{}", base_version, sep, vcs_snapshot.to_suffix()) +} + #[cfg(test)] mod tests { #[test] @@ -52,4 +239,160 @@ mod tests { ); assert_eq!(super::add_dfsg_suffix("1.2.3", Some("1.2.3")), "1.2.3+ds"); } + + #[test] + fn test_to_suffix() { + assert_eq!( + "git", + super::VcsSnapshot::Git { + date: None, + sha: None, + snapshot: None, + } + .to_suffix() + ); + assert_eq!( + "git20210101", + super::VcsSnapshot::Git { + date: Some(chrono::NaiveDate::from_ymd_opt(2021, 1, 1).unwrap()), + sha: None, + snapshot: None, + } + .to_suffix() + ); + assert_eq!( + "gitabcdefg", + super::VcsSnapshot::Git { + date: None, + sha: Some("abcdefg".to_string()), + snapshot: None, + } + .to_suffix() + ); + assert_eq!( + "git20210101.abcdefg", + super::VcsSnapshot::Git { + date: Some(chrono::NaiveDate::from_ymd_opt(2021, 1, 1).unwrap()), + sha: Some("abcdefg".to_string()), + snapshot: None, + } + .to_suffix() + ); + assert_eq!( + "git20210101.1.abcdefg", + super::VcsSnapshot::Git { + date: Some(chrono::NaiveDate::from_ymd_opt(2021, 1, 1).unwrap()), + sha: Some("abcdefg".to_string()), + snapshot: Some(1), + } + .to_suffix() + ); + assert_eq!( + "bzr123", + super::VcsSnapshot::Bzr { + revno: "123".to_string(), + } + .to_suffix() + ); + assert_eq!("svn123", super::VcsSnapshot::Svn { revno: 123 }.to_suffix()); + } + + #[test] + fn test_upstream_version_add_new_suffix_bzr() { + assert_eq!( + "1.2.3+bzr123", + super::upstream_version_add_revision( + "1.2.3", + super::VcsSnapshot::Bzr { + revno: "123".to_string() + }, + None + ) + ); + } + + #[test] + fn test_upstream_version_add_existing_suffix_bzr() { + assert_eq!( + "1.2.3+bzr124", + super::upstream_version_add_revision( + "1.2.3+bzr123", + super::VcsSnapshot::Bzr { + revno: "124".to_string() + }, + None + ) + ); + } + + #[test] + fn test_upstream_version_add_new_suffix_git() { + assert_eq!( + "1.2.3+git20210101", + super::upstream_version_add_revision( + "1.2.3", + super::VcsSnapshot::Git { + date: Some(chrono::NaiveDate::from_ymd_opt(2021, 1, 1).unwrap()), + sha: None, + snapshot: None, + }, + None + ) + ); + } + + #[test] + fn test_upstream_version_add_existing_suffix_git() { + assert_eq!( + super::VcsSnapshot::Git { + date: Some(chrono::NaiveDate::from_ymd_opt(2021, 1, 1).unwrap()), + sha: Some("abcdefa".to_string()), + snapshot: None, + }, + super::get_revision("1.2.3+git20210101.abcdefa") + .1 + .unwrap() + .1 + ); + assert_eq!( + "1.2.3+gitabcdefa", + super::upstream_version_add_revision( + "1.2.3+git20210101.1.abcdefa", + super::VcsSnapshot::Git { + date: None, + sha: Some("abcdefa".to_string()), + snapshot: None, + }, + None + ) + ); + } + + #[test] + fn test_upstream_version_add_new_suffix_svn() { + assert_eq!( + "1.2.3+svn123", + super::upstream_version_add_revision( + "1.2.3", + super::VcsSnapshot::Svn { revno: 123 }, + None + ) + ); + } + + #[test] + fn test_upstream_version_add_existing_suffix_svn() { + assert_eq!( + super::VcsSnapshot::Svn { revno: 123 }, + super::get_revision("1.2.3+svn123").1.unwrap().1 + ); + assert_eq!( + "1.2.3+svn124", + super::upstream_version_add_revision( + "1.2.3+svn123", + super::VcsSnapshot::Svn { revno: 124 }, + None + ) + ); + } }