build,debian: extract reusable Debian format primitives from build/
Move the generic components out of src/build/ into a new src/debian/ module so they can be reused independently of the build pipeline: deb822 control parsing (plus the debian/control model), file checksum registry, debian/files registry, Debian version handling and changelog entry parsing. Merge OpenPGP clearsigning into utils/gpg.rs next to the existing key discovery helper, making signing available outside of builds. Delegate changelog.rs header/footer parsing to the new debian::changelog parser, removing the duplicate regex implementation. src/build/ keeps only build-specific logic: the pipeline driver, build types, environment setup and the .buildinfo/.changes writers.
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
//! Debian version handling: splitting and validation of
|
||||
//! `[epoch:]upstream[-revision]` version strings.
|
||||
|
||||
/// A Debian version, split into its `[epoch:]upstream[-revision]` parts.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DebianVersion {
|
||||
/// Optional numeric epoch (part before the first `:`).
|
||||
pub epoch: Option<u32>,
|
||||
/// Upstream version (may itself contain `-` when there is no revision).
|
||||
pub upstream: String,
|
||||
/// Optional Debian revision (part after the last `-`).
|
||||
pub debian_revision: Option<String>,
|
||||
}
|
||||
|
||||
impl DebianVersion {
|
||||
/// Parse and validate a Debian version string.
|
||||
pub fn parse(raw: &str) -> Result<DebianVersion, String> {
|
||||
let raw = raw.trim();
|
||||
if raw.is_empty() {
|
||||
return Err("empty version string".to_string());
|
||||
}
|
||||
|
||||
let (epoch, rest) = match raw.split_once(':') {
|
||||
Some((e, r)) => {
|
||||
let epoch: u32 = e
|
||||
.parse()
|
||||
.map_err(|_| format!("invalid epoch '{}' in version '{}'", e, raw))?;
|
||||
(Some(epoch), r)
|
||||
}
|
||||
None => (None, raw),
|
||||
};
|
||||
|
||||
// The revision is everything after the last hyphen.
|
||||
let (upstream, debian_revision) = match rest.rsplit_once('-') {
|
||||
Some((u, r)) => (u.to_string(), Some(r.to_string())),
|
||||
None => (rest.to_string(), None),
|
||||
};
|
||||
|
||||
if upstream.is_empty() {
|
||||
return Err(format!("missing upstream version in '{}'", raw));
|
||||
}
|
||||
for c in upstream.chars() {
|
||||
if !(c.is_ascii_alphanumeric()
|
||||
|| matches!(c, '.' | '+' | '-' | '~' | ':')
|
||||
|| !c.is_ascii())
|
||||
{
|
||||
return Err(format!("invalid character '{}' in version '{}'", c, raw));
|
||||
}
|
||||
}
|
||||
if let Some(rev) = &debian_revision {
|
||||
for c in rev.chars() {
|
||||
if !(c.is_ascii_alphanumeric() || matches!(c, '.' | '+' | '~') || !c.is_ascii()) {
|
||||
return Err(format!(
|
||||
"invalid character '{}' in revision of version '{}'",
|
||||
c, raw
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(DebianVersion {
|
||||
epoch,
|
||||
upstream,
|
||||
debian_revision,
|
||||
})
|
||||
}
|
||||
|
||||
/// Full version string, including the epoch (`[epoch:]upstream[-rev]`).
|
||||
pub fn full(&self) -> String {
|
||||
match (&self.epoch, &self.debian_revision) {
|
||||
(Some(e), Some(r)) => format!("{}:{}-{}", e, self.upstream, r),
|
||||
(Some(e), None) => format!("{}:{}", e, self.upstream),
|
||||
(None, Some(r)) => format!("{}-{}", self.upstream, r),
|
||||
(None, None) => self.upstream.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Version string without the epoch (`upstream[-rev]`), used in artifact
|
||||
/// file names.
|
||||
pub fn no_epoch(&self) -> String {
|
||||
match &self.debian_revision {
|
||||
Some(r) => format!("{}-{}", self.upstream, r),
|
||||
None => self.upstream.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn version_splitting() {
|
||||
let v = DebianVersion::parse("1.2.3-4ubuntu5").unwrap();
|
||||
assert_eq!(v.epoch, None);
|
||||
assert_eq!(v.upstream, "1.2.3");
|
||||
assert_eq!(v.debian_revision.as_deref(), Some("4ubuntu5"));
|
||||
assert_eq!(v.full(), "1.2.3-4ubuntu5");
|
||||
assert_eq!(v.no_epoch(), "1.2.3-4ubuntu5");
|
||||
|
||||
let v = DebianVersion::parse("3:2.10-3").unwrap();
|
||||
assert_eq!(v.epoch, Some(3));
|
||||
assert_eq!(v.upstream, "2.10");
|
||||
assert_eq!(v.no_epoch(), "2.10-3");
|
||||
assert_eq!(v.full(), "3:2.10-3");
|
||||
|
||||
let v = DebianVersion::parse("1.0").unwrap();
|
||||
assert_eq!(v.debian_revision, None);
|
||||
assert_eq!(v.no_epoch(), "1.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_validation() {
|
||||
assert!(DebianVersion::parse("").is_err());
|
||||
assert!(DebianVersion::parse(":1.0").is_err());
|
||||
assert!(DebianVersion::parse("a:_b").is_err());
|
||||
assert!(DebianVersion::parse("1.0").is_ok());
|
||||
assert!(DebianVersion::parse("1.0~rc1-2").is_ok());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user