diff --git a/src/debian/version.rs b/src/debian/version.rs index 578bf1a..47f7e95 100644 --- a/src/debian/version.rs +++ b/src/debian/version.rs @@ -48,6 +48,12 @@ impl DebianVersion { } } if let Some(rev) = &debian_revision { + if rev.is_empty() { + // dpkg rejects a trailing hyphen: "bad syntax: revision + // number is empty". Native versions (no `-` at all) are + // handled above and stay valid. + return Err(format!("empty debian revision in '{}'", raw)); + } for c in rev.chars() { if !(c.is_ascii_alphanumeric() || matches!(c, '.' | '+' | '~') || !c.is_ascii()) { return Err(format!( @@ -329,6 +335,27 @@ mod tests { assert!(DebianVersion::parse("1.0~rc1-2").is_ok()); } + /// dpkg rejects a trailing `-` ("bad syntax: revision number is + /// empty") but accepts `1.0--1`, where the revision is the text after + /// the *last* hyphen (upstream `1.0-` + revision `1`). + #[test] + fn version_empty_revision() { + let err = DebianVersion::parse("1.0-").unwrap_err(); + assert!(err.contains("empty"), "unexpected message: {err}"); + + assert!(DebianVersion::parse("1.0-").is_err()); + // Epoch variants take the same path. + assert!(DebianVersion::parse("3:1.0-").is_err()); + assert!(DebianVersion::parse("1.0-1").is_ok()); + // Native versions (no revision at all) are still fine. + assert!(DebianVersion::parse("1.0").is_ok()); + assert!(DebianVersion::parse("3:1.0").is_ok()); + + let v = DebianVersion::parse("1.0--1").unwrap(); + assert_eq!(v.upstream, "1.0-"); + assert_eq!(v.debian_revision.as_deref(), Some("1")); + } + fn cmp_sign(a: &DebianVersion, b: &DebianVersion) -> i32 { match a.cmp(b) { std::cmp::Ordering::Less => -1,