Versioning

Parse a complex version string.

semver-badge cat-config-badge

Constructs a semver::Version from a complex version string using Version::parse. The string contains pre-release and build metadata as defined in the Semantic Versioning Specification.

Note that, in accordance with the Specification, build metadata is parsed and considered when comparing versions. In other words, two versions are not equal even if their build strings differ.

use std::error::Error;
use semver::{BuildMetadata, Prerelease, Version};

fn main() -> Result<(), Box<dyn Error>> {
    let version_str = "1.0.49-125+g72ee7853";
    let parsed_version = Version::parse(version_str)?;

    assert_ne!(
        parsed_version,
        Version {
            major: 1,
            minor: 0,
            patch: 49,
            pre: Prerelease::new("125").unwrap(),
            build: BuildMetadata::new("7208347").unwrap(),
        }
    );

    assert_eq!(
        parsed_version.build,
        BuildMetadata::new("g72ee7853").unwrap(),
    );

    let serialized_version = parsed_version.to_string();
    assert_eq!(&serialized_version, version_str);

    Ok(())
}

Check if given version is pre-release.

semver-badge cat-config-badge

Given two versions, the Prerelease field will be EMPTY if there is not an optional pre-release identifier in the version string.

use std::error::Error;
use semver::{Version, Prerelease};

fn main() -> Result<(), Box<dyn Error>> {
    let version_1 = Version::parse("1.0.0-alpha")?;
    let version_2 = Version::parse("1.0.0")?;

    assert_ne!(version_1.pre, Prerelease::EMPTY);
    assert_eq!(version_2.pre, Prerelease::EMPTY);

    Ok(())
}

Find the latest version satisfying given range

semver-badge cat-config-badge

Given a list of version &strs, finds the latest semver::Version. semver::VersionReq filters the list with VersionReq::matches. Also demonstrates semver pre-release preferences.

use std::error::Error;
use semver::{VersionReq, Version};

fn find_max_matching_version<'a, I>(version_req_str: &str, iterable: I) -> Result<Option<Version>, Box<dyn Error>>
where
    I: IntoIterator<Item = &'a str>,
{
    let vreq = VersionReq::parse(version_req_str)?;

    Ok(
        iterable
            .into_iter()
            .filter_map(|s| Version::parse(s).ok())
            .filter(|s| vreq.matches(s))
            .max(),
    )
}

fn main() -> Result<(), Box<dyn Error>> {
    assert_eq!(
        find_max_matching_version("<= 1.0.0", vec!["0.9.0", "1.0.0", "1.0.1"])?,
        Some(Version::parse("1.0.0")?)
    );

    assert_eq!(
        find_max_matching_version(
            ">1.2.3-alpha.3",
            vec![
                "1.2.3-alpha.3",
                "1.2.3-alpha.4",
                "1.2.3-alpha.10",
                "1.2.3-beta.4",
                "3.4.5-alpha.9",
            ]
        )?,
        Some(Version::parse("1.2.3-beta.4")?)
    );

    Ok(())
}

Check external command version for compatibility

semver-badge cat-text-processing-badge cat-os-badge

Runs git --version using Command, then parses the version number into a semver::Version using Version::parse. VersionReq::matches compares semver::VersionReq to the parsed version. The command output resembles "git version x.y.z".

use anyhow::{anyhow, Result};
use std::process::Command;
use semver::{Version, VersionReq};

fn main() -> Result<()> {
    let version_constraint = "> 1.12.0";
    let version_test = VersionReq::parse(version_constraint)?;

    let output = Command::new("git").arg("--version").output()?;
    if !output.status.success() {
        return Err(anyhow!("Command executed with failing error code"));
    }
    let stdout = String::from_utf8(output.stdout)?;
    let version = stdout.split(" ").last().ok_or_else(|| {
        anyhow!("Invalid command output")
    })?.trim_end();

    let parsed_version = Version::parse(version)?;
    if !version_test.matches(&parsed_version) {
        return Err(anyhow!("Command version lower than minimum supported version (found {}, need {})",
            parsed_version, version_constraint));
    } else {
        println!("git version constraint '{version_constraint}' satisfied for: {version}");
    }

    Ok(())
}