Versioning
Parse and increment a version string
Constructs a semver::Version
⮳ from a string literal using semver::Version::parse
⮳ then increments it by patch, minor, and major version number one by one.
Note that in accordance with the semantic versioning specification`⮳, incrementing the minor version number resets the patch version number to 0 and incrementing the major version number resets both the minor and patch version numbers to 0.
use anyhow::Result; use semver::Version; fn main() -> Result<()> { let parsed_version = Version::parse("0.2.6")?; // Note: a SemVer version must have exactly three components assert_eq!( parsed_version, Version { major: 0, minor: 2, patch: 6, pre: semver::Prerelease::EMPTY, build: semver::BuildMetadata::EMPTY, } ); // TODO increment_*() no longer exist; remove example? // parsed_version.increment_patch(); // assert_eq!(parsed_version.to_string(), "0.2.7"); // println!("New patch release: v{}", parsed_version); // parsed_version.increment_minor(); // assert_eq!(parsed_version.to_string(), "0.3.0"); // println!("New minor release: v{}", parsed_version); // parsed_version.increment_major(); // assert_eq!(parsed_version.to_string(), "1.0.0"); // println!("New major release: v{}", parsed_version); Ok(()) }
Parse a complex version string
Constructs a semver::Version
⮳ from a complex version string using semver::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 but not considered when comparing versions. In other words, two versions may be equal even if their build strings differ.
use anyhow::Result; use semver::BuildMetadata; use semver::Prerelease; use semver::Version; fn main() -> Result<()> { let version_str = "1.0.49-125+g72ee7853"; let parsed_version = Version::parse(version_str)?; assert_eq!( parsed_version, Version { major: 1, minor: 0, patch: 49, pre: Prerelease::new("125")?, build: BuildMetadata::new("g72ee7853")?, } ); let serialized_version = parsed_version.to_string(); assert_eq!(&serialized_version, version_str); Ok(()) }
Check if given version is pre-release
Given two versions, semver::Version
⮳ asserts that one is pre-release and the other is not.
use anyhow::Result; use semver::Version; fn main() -> Result<()> { let version_1 = Version::parse("1.0.0-alpha")?; let version_2 = Version::parse("1.0.0")?; assert!(!version_1.pre.is_empty()); assert!(version_2.pre.is_empty()); Ok(()) }
Find the latest version satisfying given range
Given a list of version &strs, finds the latest semver::Version
⮳
semver::VersionReq
⮳ filters the list with semver::VersionReq::matches
⮳ Also demonstrates semver
⮳ pre-release preferences.
use anyhow::Result; use semver::Version; use semver::VersionReq; fn find_max_matching_version<'a, I>( version_req_str: &str, iterable: I, ) -> Result<Option<Version>> 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<()> { 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
Runs git --version
using std::process::Command
⮳ then parses the version number into a
semver::Version
⮳ using semver::Version::parse
⮳ semver::VersionReq::matches
⮳ compares
semver::VersionReq
to the parsed version. The command output resembles "git version x.y.z".
use std::process::Command; use anyhow::anyhow; use anyhow::bail; use anyhow::Context; use anyhow::Result; use semver::Version; use semver::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() { bail!("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(); // remove any extraneous newlines let parsed_version = Version::parse(version).context(format!("version: {}", version))?; if !version_test.matches(&parsed_version) { bail!( "Command version lower than minimum supported version (found {}, need {})", parsed_version, version_constraint ); } Ok(()) }