use std::fs;
use std::path::Path;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub(crate) struct Version {
pub major: u32,
pub minor: u32,
pub patch: u32,
}
impl std::fmt::Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}
pub(crate) const MIN_SUPPORTED: Version = Version {
major: 4,
minor: 13,
patch: 3,
};
pub(crate) fn parse_dotted(s: &str) -> Option<Version> {
let mut parts = s.trim().split('.');
let major = parts.next()?.parse().ok()?;
let minor = parts.next()?.parse().ok()?;
let patch = parts.next().unwrap_or("0").parse().ok()?;
Some(Version {
major,
minor,
patch,
})
}
pub(crate) fn parse_header(path: &Path) -> Option<Version> {
let contents = fs::read_to_string(path).ok()?;
let major = find_macro_value(&contents, "Z3_MAJOR_VERSION")?;
let minor = find_macro_value(&contents, "Z3_MINOR_VERSION")?;
let patch = find_macro_value(&contents, "Z3_BUILD_NUMBER").unwrap_or(0);
Some(Version {
major,
minor,
patch,
})
}
fn find_macro_value(contents: &str, name: &str) -> Option<u32> {
for line in contents.lines() {
let line = line.trim();
let Some(rest) = line.strip_prefix("#define") else {
continue;
};
let mut tokens = rest.split_whitespace();
if tokens.next() == Some(name) {
return tokens.next()?.parse().ok();
}
}
None
}
pub(crate) fn parse_cli_output(s: &str) -> Option<Version> {
let after = s.split("version").nth(1)?;
let token = after.split_whitespace().next()?;
parse_dotted(token)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_dotted_versions() {
assert_eq!(
parse_dotted("4.13.3"),
Some(Version {
major: 4,
minor: 13,
patch: 3
})
);
assert_eq!(
parse_dotted("5.0"),
Some(Version {
major: 5,
minor: 0,
patch: 0
})
);
assert_eq!(parse_dotted("not-a-version"), None);
}
#[test]
fn parses_cli_output() {
assert_eq!(
parse_cli_output("Z3 version 4.13.3 - 64 bit"),
Some(Version {
major: 4,
minor: 13,
patch: 3
})
);
assert_eq!(
parse_cli_output("Z3 version 5.0.0"),
Some(Version {
major: 5,
minor: 0,
patch: 0
})
);
assert_eq!(parse_cli_output("not z3 output"), None);
}
}