use crate::errors::{Error, Result};
use regex::Regex;
use std::sync::LazyLock;
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Clone)]
pub struct Version {
pub major: u64,
pub minor: u64,
pub patch: u64,
pub build: u64,
}
impl Version {
pub const fn new(major: u64, minor: u64, patch: u64, build: u64) -> Self {
Version {
major,
minor,
patch,
build,
}
}
}
impl Version {
pub fn supports_partition_scan(&self) -> bool {
self >= &Version::new(4, 9, 0, 3)
}
pub fn supports_query_show(&self) -> bool {
self >= &Version::new(5, 7, 0, 0)
}
pub fn supports_batch_any(&self) -> bool {
self >= &Version::new(6, 0, 0, 0)
}
pub fn supports_partition_query(&self) -> bool {
self >= &Version::new(6, 0, 0, 0)
}
pub fn supports_app_id(&self) -> bool {
self >= &Version::new(8, 1, 0, 0)
}
}
#[derive(Debug)]
pub struct VersionParser<'a> {
s: &'a str,
}
impl<'a> VersionParser<'a> {
pub const fn new(s: &'a str) -> Self {
VersionParser { s }
}
pub fn parse(&self) -> Result<Version> {
static RE: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^(?P<major>(\d+))\.(?P<minor>(\d+))\.(?P<patch>(\d+))\.(?P<build>(\d+))")
.unwrap()
});
if !RE.is_match(self.s) {
return Err(Error::ClientError(format!(
"Could not parse node version string `{}`",
self.s
)));
}
let (major, minor, patch, build) = RE
.captures(self.s)
.map(|caps| {
(
caps.name("major").unwrap().as_str(),
caps.name("minor").unwrap().as_str(),
caps.name("patch").unwrap().as_str(),
caps.name("build").unwrap().as_str(),
)
})
.unwrap();
let (major, minor, patch, build) = (
major.parse::<u64>().unwrap(),
minor.parse::<u64>().unwrap(),
patch.parse::<u64>().unwrap(),
build.parse::<u64>().unwrap(),
);
Ok(Version {
major,
minor,
patch,
build,
})
}
}
#[cfg(test)]
mod tests {
use super::{Version, VersionParser};
#[test]
fn comparisons() {
assert!(Version::new(1, 0, 0, 0) < Version::new(1, 0, 0, 1));
assert!(Version::new(1, 0, 0, 0) <= Version::new(1, 0, 0, 1));
assert!(Version::new(2, 1, 3, 2) > Version::new(2, 1, 3, 1));
assert!(Version::new(2, 1, 3, 2) >= Version::new(2, 1, 3, 1));
assert!(Version::new(4, 3, 2, 1) == Version::new(4, 3, 2, 1));
assert!(Version::new(4, 3, 2, 1) <= Version::new(4, 3, 2, 1));
assert!(Version::new(4, 3, 2, 1) >= Version::new(4, 3, 2, 1));
}
#[test]
fn parse() {
assert_eq!(
Version::new(1, 2, 3, 4),
VersionParser::new("1.2.3.4").parse().unwrap()
);
assert_eq!(
Version::new(1, 2, 3, 4),
VersionParser::new("1.2.3.4-asdfasdf").parse().unwrap()
);
assert_eq!(
Version::new(1111, 2222, 333, 14),
VersionParser::new("1111.2222.333.14").parse().unwrap()
);
assert_eq!(
Version::new(1111, 2222, 333, 14),
VersionParser::new("1111.2222.333.14.adtrwadfdsfk")
.parse()
.unwrap()
);
assert_eq!(
Version::new(1111, 2222, 333, 14),
VersionParser::new("1111.2222.333.14adtrwadfdsfk")
.parse()
.unwrap()
);
let invalid = vec![
".1.2.3.4",
"1.2.3-asdfasdf",
"1111..333.14",
"1111.a2222.333.14-asdfasdf",
];
for iv in invalid {
assert!(VersionParser::new(iv).parse().is_err());
}
}
}