use crate::error::Error;
pub fn parse(version: &str) -> Result<(u64, u64, u64), Error> {
let bad = || Error::BadVersion(version.to_string());
let parts: [&str; 3] = version
.split('.')
.collect::<Vec<_>>()
.try_into()
.map_err(|_| bad())?;
let mut nums = [0u64; 3];
for (slot, p) in nums.iter_mut().zip(parts) {
if p.is_empty() || !p.bytes().all(|b| b.is_ascii_digit()) {
return Err(bad());
}
if p.len() > 1 && p.starts_with('0') {
return Err(bad());
}
*slot = p.parse::<u64>().map_err(|_| bad())?;
}
Ok((nums[0], nums[1], nums[2]))
}
pub fn parse_major(version: &str) -> Result<u64, Error> {
parse(version).map(|(major, _, _)| major)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_major_of_well_formed() {
assert_eq!(parse_major("0.1.0").unwrap(), 0);
assert_eq!(parse_major("12.4.7").unwrap(), 12);
}
#[test]
fn rejects_non_major_minor_patch() {
assert!(parse_major("3").is_err()); assert!(parse_major("0.1").is_err()); assert!(parse_major("1.2.3.4").is_err()); assert!(parse_major("0.1.0-rc1").is_err()); }
#[test]
fn rejects_leading_zeros() {
assert!(parse_major("01.2.0").is_err());
assert!(parse_major("1.02.0").is_err());
assert!(parse_major("1.2.00").is_err());
assert_eq!(parse("0.0.0").unwrap(), (0, 0, 0));
assert_eq!(parse("10.20.30").unwrap(), (10, 20, 30));
}
#[test]
fn rejects_garbage() {
assert!(parse_major("").is_err());
assert!(parse_major("x.y.z").is_err());
assert!(parse_major("-1.0.0").is_err());
}
}