pub const PROTOCOL_VERSION: &str = env!("PROTOCOL_VERSION");
pub const MIN_COMPATIBLE_VERSION: &str = env!("MIN_COMPATIBLE_VERSION");
#[derive(Debug, thiserror::Error)]
pub enum VersionError {
#[error("Invalid version format: {0}")]
InvalidFormat(String),
#[error("Version component parse error: {0}")]
ParseError(String),
}
pub fn parse_version(version: &str) -> Result<(u32, u32, u32), VersionError> {
let parts: Vec<&str> = version.split('.').collect();
if parts.len() != 3 {
return Err(VersionError::InvalidFormat(format!(
"Expected format 'MAJOR.MINOR.PATCH', got '{}'",
version
)));
}
let major = parts[0]
.parse::<u32>()
.map_err(|e| VersionError::ParseError(format!("major: {}", e)))?;
let minor = parts[1]
.parse::<u32>()
.map_err(|e| VersionError::ParseError(format!("minor: {}", e)))?;
let patch = parts[2]
.parse::<u32>()
.map_err(|e| VersionError::ParseError(format!("patch: {}", e)))?;
Ok((major, minor, patch))
}
pub fn is_compatible(actual: &str, minimum: &str) -> Result<bool, VersionError> {
let (actual_major, actual_minor, _) = parse_version(actual)?;
let (min_major, min_minor, _) = parse_version(minimum)?;
if actual_major != min_major {
return Ok(false);
}
Ok(actual_minor >= min_minor)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_version_constants_injected() {
assert!(!PROTOCOL_VERSION.is_empty(), "PROTOCOL_VERSION should not be empty");
assert!(
!MIN_COMPATIBLE_VERSION.is_empty(),
"MIN_COMPATIBLE_VERSION should not be empty"
);
assert!(
parse_version(PROTOCOL_VERSION).is_ok(),
"PROTOCOL_VERSION should be valid semver"
);
assert!(
parse_version(MIN_COMPATIBLE_VERSION).is_ok(),
"MIN_COMPATIBLE_VERSION should be valid semver"
);
}
#[test]
fn test_parse_version() {
assert_eq!(parse_version("1.0.0").unwrap(), (1, 0, 0));
assert_eq!(parse_version("2.5.10").unwrap(), (2, 5, 10));
assert!(parse_version("1.0").is_err());
assert!(parse_version("1.0.0.0").is_err());
assert!(parse_version("abc").is_err());
}
#[test]
fn test_is_compatible() {
assert!(is_compatible("1.2.0", "1.0.0").unwrap());
assert!(is_compatible("1.5.0", "1.2.0").unwrap());
assert!(is_compatible("1.0.5", "1.0.3").unwrap());
assert!(is_compatible("1.2.10", "1.2.5").unwrap());
assert!(is_compatible("1.0.0", "1.0.0").unwrap());
assert!(is_compatible("2.5.3", "2.5.3").unwrap());
assert!(!is_compatible("2.0.0", "1.0.0").unwrap());
assert!(!is_compatible("1.0.0", "2.0.0").unwrap());
assert!(!is_compatible("1.0.0", "1.1.0").unwrap());
assert!(!is_compatible("1.2.0", "1.5.0").unwrap());
assert!(is_compatible("1.0.3", "1.0.5").unwrap());
assert!(is_compatible("2.5.0", "2.5.1").unwrap());
assert!(is_compatible("1.0.0", "1.0.99").unwrap());
assert!(is_compatible("1.0.99", "1.0.0").unwrap());
}
}