Skip to main content

Version

Enum Version 

Source
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 v prefix or fewer than three parts)
  • dot-separated numeric (e.g., date-based 2024.01.15 or four-part 1.2.3.4)
  • PEP 440 pre/post/dev releases and epochs (dominant in Python SBOMs)
  • Debian epoch:upstream-revision and RPM epoch: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.

Fields

§epoch: u64
§upstream: String
§revision: 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.

Fields

§epoch: u64
§version: String
§release: String
§

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

Source

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(_)));
Source

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"));
Source

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 dpkg version-comparison algorithm
  • Rpm vs Rpm: epoch (numeric), then version, then release, via rpm’s rpmvercmp algorithm
  • 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 stays None
  • 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);
Source

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§

Source§

impl Clone for Version

Source§

fn clone(&self) -> Version

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Version

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for Version

Source§

impl PartialEq for Version

Source§

fn eq(&self, other: &Version) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Version

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.