use anyhow::{anyhow, Result};
use regex::{Regex, RegexBuilder};
use semver::Version;
lazy_static! {
static ref VERSION_RX: Regex = RegexBuilder::new(
"(?P<version>
(
# version with possible pre-release info. Must contain at
# least two components to avoid ambiguity.
[0-9]+(\\.[0-9]+)+(-[a-z.0-9]+)?
)|(
# version with no pre-release info. Supports one component
# version numbers.
[0-9]+(\\.[0-9]+)*
)
)$"
)
.ignore_whitespace(true)
.build()
.unwrap();
static ref LEADING_0_RX: Regex = Regex::new(r"0+([0-9])").unwrap();
}
fn count_chars(txt: &str, wanted: char) -> usize {
txt.chars().filter(|&x| x == wanted).count()
}
pub fn version_from_tag(tag: &str) -> Result<Version> {
let version_str = VERSION_RX
.captures(tag)
.map(|captures| captures["version"].to_string());
let mut version_str = match version_str {
Some(x) => x.to_string(),
None => {
return Err(anyhow!("Can't find version number in '{}'", tag));
}
};
while count_chars(&version_str, '.') < 2 {
version_str.push_str(".0");
}
let version_str = LEADING_0_RX.replace_all(&version_str, "$1");
let version = Version::parse(&version_str)?;
Ok(version)
}
#[cfg(test)]
mod tests {
use super::*;
use yare::parameterized;
#[parameterized(
skip_prefix1 = { "v1.12.0", "1.12.0"},
skip_prefix2 = { "foo14-1.12.0", "1.12.0"},
prerelease1 = { "forgejo-1.2.3-0", "1.2.3-0"},
prerelease2 = { "v0.2.2-pre", "0.2.2-pre"},
missing_component1 = {"1", "1.0.0"},
missing_component2 = {"1.12", "1.12.0"},
skip_leading_zero1 = {"01.002.003", "1.2.3"},
skip_leading_zero2 = {"00.1", "0.1.0"},
)]
fn check_version_from_tag_success(tag: &str, expected_str: &str) {
let expected = Version::parse(expected_str).unwrap();
assert_eq!(version_from_tag(tag).unwrap(), expected);
}
#[test]
fn check_version_from_tag_fails() {
assert!(version_from_tag("foo").is_err());
}
}