use crate::error::{Error, Result};
use lazy_static::lazy_static;
use regex::Regex;
use super::Semver;
lazy_static! {
static ref FULL_REGEX: Regex =
Regex::new(r"^(\d+(?:\.\d+)*)([a-z])?(?:-([0-9A-Za-z-\.]+))?(?:\+([0-9A-Za-z-\.]+))?$")
.unwrap();
static ref SHORT_REGEX: Regex = Regex::new(r"^\d+(\.\d+)?([-\+].*)?$").unwrap();
}
impl Semver {
pub fn parse(semver: &str) -> Result<Self> {
let raw = semver.trim_start_matches('v').to_string();
let captures = FULL_REGEX
.captures(&raw)
.ok_or_else(|| Error::Semver("invalid semver".into()))?;
let mut components: Vec<usize> = captures
.get(1)
.ok_or_else(|| Error::Semver("regex failure".into()))?
.as_str()
.split('.')
.map(|s| {
s.parse::<usize>()
.map_err(|_| Error::Semver("invalid digit".into()))
})
.collect::<Result<Vec<usize>>>()?;
if let Some(letter) = captures.get(2) {
let letter = letter
.as_str()
.chars()
.next()
.ok_or_else(|| Error::Semver("not a character".into()))?
as usize
- 'a' as usize
+ 1;
components.push(letter);
}
let major = *components
.first()
.ok_or_else(|| Error::Semver("string is too short".into()))?;
let minor = *components.get(1).unwrap_or(&0);
let patch = *components.get(2).unwrap_or(&0);
let prerelease = if let Some(pr) = captures.get(3) {
pr.as_str().split('.').map(|s| s.to_string()).collect()
} else {
vec![]
};
let build = if let Some(b) = captures.get(4) {
b.as_str().split('.').map(|s| s.to_string()).collect()
} else {
vec![]
};
let raw = if SHORT_REGEX.is_match(&raw) {
let mut r = format!("{major}.{minor}.{patch}");
if !prerelease.is_empty() {
r.push_str(&format!("-{}", prerelease.join(".")));
}
if !build.is_empty() {
r.push_str(&format!("+{}", build.join(".")));
}
r
} else {
raw
};
Ok(Self {
components,
major,
minor,
patch,
prerelease,
build,
raw,
})
}
pub fn from(input: (usize, usize, usize)) -> Result<Self> {
Self::parse(&format!("{}.{}.{}", input.0, input.1, input.2))
}
}