pub enum Version {
Semver(Version),
Numeric(Vec<u64>),
Pep440(Pep440),
Deb {
epoch: u64,
upstream: String,
revision: String,
},
Rpm {
epoch: u64,
version: String,
release: String,
},
Maven(String),
Opaque(String),
}Expand description
parsed version representation for lenient comparison.
covers the common version formats found in SBOMs:
- standard semver (possibly with
vprefix or fewer than three parts) - dot-separated numeric (e.g., date-based
2024.01.15or four-part1.2.3.4) - PEP 440 pre/post/dev releases and epochs (dominant in Python SBOMs)
- Debian
epoch:upstream-revisionand RPMepoch:version-release(dominant in OS/container SBOMs) - Maven versions, whose qualifiers (
1.0-SNAPSHOT,2.0-rc1) look like semver pre-releases but are ranked by a named order - opaque strings that cannot be compared
Variants§
Semver(Version)
parseable as semver (with lenient parsing: v/V prefix stripped,
one- or two-part versions padded to three parts).
Numeric(Vec<u64>)
dot-separated numeric segments that don’t qualify as semver (e.g., four-part versions or versions with leading zeros).
Pep440(Pep440)
PEP 440 (Python) version carrying an epoch, pre-release, post-release or dev-release segment, ordered per the PEP.
Deb
Debian-style epoch:upstream-revision version, compared with the Debian
dpkg algorithm. a N! epoch prefix is accepted too, for strings
Pep440 declines. an absent epoch is 0 and an
absent revision is the empty string.
Rpm
RPM epoch:version-release, compared with rpm’s own rpmvercmp
algorithm, which disagrees with the Debian one on ordinary inputs. an
absent epoch is 0; an absent release is the empty string and sorts
below every release, including 0. only
parse_for_ecosystem produces this
variant — the shape alone does not distinguish an RPM version from a
Debian one.
Maven(String)
Maven (Java) version, compared with Maven’s own version-order algorithm,
under which 1.0-SNAPSHOT sorts below 1.0 but 1.0-sp above it. only
parse_for_ecosystem produces this
variant — the shape alone does not distinguish a Maven qualifier from a
semver pre-release or a Debian revision.
Opaque(String)
non-parseable version string where ordering cannot be determined.
Implementations§
Source§impl Version
impl Version
Sourcepub fn parse_lenient(s: &str) -> Self
pub fn parse_lenient(s: &str) -> Self
parses a version string leniently.
tries semver first (stripping v/V prefix and padding one- or
two-part versions), then dot-separated numeric, then PEP 440, then
Debian-style epoch/revision versions, then falls back to
Opaque.
the shape alone does not always identify the format; when the
component’s ecosystem is known, prefer
parse_for_ecosystem.
§Examples
use sbom_model::versions::Version;
assert!(matches!(Version::parse_lenient("1.2.3"), Version::Semver(_)));
assert!(matches!(Version::parse_lenient("v1.2"), Version::Semver(_)));
assert!(matches!(Version::parse_lenient("2024.01.15"), Version::Numeric(_)));
assert!(matches!(Version::parse_lenient("4.2.0rc1"), Version::Pep440(_)));
assert!(matches!(Version::parse_lenient("2:1.0-3"), Version::Deb { .. }));
assert!(matches!(Version::parse_lenient("abc"), Version::Opaque(_)));Sourcepub fn parse_for_ecosystem(ecosystem: Option<&str>, s: &str) -> Self
pub fn parse_for_ecosystem(ecosystem: Option<&str>, s: &str) -> Self
parses a version string with the rules of the ecosystem it came from.
ecosystem is a purl package type (the value of
Component::ecosystem). None, or a type
with no dedicated ruleset, is exactly parse_lenient.
deb versions are read as Deb and ordered by the
dpkg algorithm, rpm versions as Rpm and ordered by
rpm’s rpmvercmp, maven versions as Maven and
ordered by Maven’s version-order algorithm. a leading v/V is
stripped, as parse_lenient does; a string that
is still not a valid version for that ecosystem is
Opaque rather than being retried as semver.
§Examples
use std::cmp::Ordering;
use sbom_model::versions::Version;
// `1ubuntu2` is a Debian revision, not a semver pre-release
let old = Version::parse_for_ecosystem(Some("deb"), "1.2.3-1ubuntu2");
let new = Version::parse_for_ecosystem(Some("deb"), "1.2.3-2");
assert_eq!(old.partial_cmp_lenient(&new), Some(Ordering::Less));
// rpm ranks a numeric segment above an alpha one, dpkg the other way
let old = Version::parse_for_ecosystem(Some("rpm"), "1.a");
let new = Version::parse_for_ecosystem(Some("rpm"), "1.1");
assert_eq!(old.partial_cmp_lenient(&new), Some(Ordering::Less));
// a Maven snapshot precedes its release, and `sp` follows it
let snapshot = Version::parse_for_ecosystem(Some("maven"), "1.0-SNAPSHOT");
let release = Version::parse_for_ecosystem(Some("maven"), "1.0");
let patched = Version::parse_for_ecosystem(Some("maven"), "1.0-sp1");
assert_eq!(snapshot.partial_cmp_lenient(&release), Some(Ordering::Less));
assert_eq!(patched.partial_cmp_lenient(&release), Some(Ordering::Greater));
let guessed = Version::parse_for_ecosystem(None, "1.2.3-1ubuntu2");
assert_eq!(guessed, Version::parse_lenient("1.2.3-1ubuntu2"));Sourcepub fn partial_cmp_lenient(&self, other: &Self) -> Option<Ordering>
pub fn partial_cmp_lenient(&self, other: &Self) -> Option<Ordering>
orders two versions, returning None when the ordering is unknown.
comparison strategy depends on the variant pair:
- Semver vs Semver: semver precedence ordering (including pre-release; build metadata is ignored per SemVer §10)
- Numeric vs Numeric: segment-by-segment with implicit zero padding
- Semver vs Numeric (either direction): extracts
[major, minor, patch]from the semver side and compares as numeric segments - Deb vs Deb: epoch (numeric), then upstream, then revision, via the
Debian
dpkgversion-comparison algorithm - Rpm vs Rpm: epoch (numeric), then version, then release, via rpm’s
rpmvercmpalgorithm - Maven vs Maven: item by item, via Maven’s version-order algorithm. a version nesting past the parser’s depth cap is declined
- Pep440 against Pep440, Semver or Numeric (either direction): the
other side is read as a PEP 440 version and both are ordered per PEP
440. a semver pre-release that isn’t a PEP 440 suffix (say
1.0.0-foo.bar) has no PEP 440 reading, so that pair staysNone - Any other pair (including any Opaque, any two of Deb, Rpm and
Maven, or any of them against a semver/numeric/PEP 440 version):
None
deliberately weaker than PartialOrd: even two identical
Opaque versions compare None.
which arm applies depends on how each side was parsed:
parse_for_ecosystem puts both sides of a
known ecosystem in the same variant, where
parse_lenient can infer different ones.
§Examples
use std::cmp::Ordering;
use sbom_model::versions::Version;
let a = Version::parse_lenient("2.0.0");
let b = Version::parse_lenient("1.5.0");
assert_eq!(a.partial_cmp_lenient(&b), Some(Ordering::Greater));
let opaque = Version::parse_lenient("deadbeef");
assert_eq!(a.partial_cmp_lenient(&opaque), None);Sourcepub fn is_downgrade(&self, new: &Self) -> bool
pub fn is_downgrade(&self, new: &Self) -> bool
returns true if new is a downgrade from self.
a pair whose ordering is unknown is not a downgrade; see
partial_cmp_lenient for the per-variant
comparison rules.
§Examples
use sbom_model::versions::Version;
let old = Version::parse_lenient("2.0.0");
let new = Version::parse_lenient("1.5.0");
assert!(old.is_downgrade(&new));
let old = Version::parse_lenient("1.0.0");
let new = Version::parse_lenient("2.0.0");
assert!(!old.is_downgrade(&new));Trait Implementations§
impl Eq for Version
impl StructuralPartialEq for Version
Auto Trait Implementations§
impl Freeze for Version
impl RefUnwindSafe for Version
impl Send for Version
impl Sync for Version
impl Unpin for Version
impl UnsafeUnpin for Version
impl UnwindSafe for Version
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.