mirror of
https://github.com/loot/loot-condition-interpreter.git
synced 2026-07-27 14:16:09 -07:00
Add support for product_version() conditions
This checks the product version field of executables instead of their file version field.
This commit is contained in:
+60
-5
@@ -173,18 +173,30 @@ fn get_version(state: &State, file_path: &Path) -> Result<Option<Version>, Error
|
||||
if state.game_type.is_plugin_filename(file_path) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Version::read_file_version(file_path).map(|v| Some(v))
|
||||
Version::read_file_version(file_path).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_version(
|
||||
fn get_product_version(file_path: &Path) -> Result<Option<Version>, Error> {
|
||||
if file_path.exists() {
|
||||
Version::read_product_version(file_path).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_version<F>(
|
||||
state: &State,
|
||||
file_path: &Path,
|
||||
given_version: &str,
|
||||
comparator: ComparisonOperator,
|
||||
) -> Result<bool, Error> {
|
||||
read_version: F,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
F: Fn(&State, &Path) -> Result<Option<Version>, Error>,
|
||||
{
|
||||
let file_path = resolve_path(state, file_path);
|
||||
let actual_version = match get_version(state, &file_path)? {
|
||||
let actual_version = match read_version(state, &file_path)? {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Ok(comparator == ComparisonOperator::NotEqual
|
||||
@@ -223,7 +235,10 @@ impl Function {
|
||||
Function::Many(p, r) => evaluate_many(state, p, r),
|
||||
Function::ManyActive(r) => evaluate_many_active(state, r),
|
||||
Function::Checksum(path, crc) => evaluate_checksum(state, path, *crc),
|
||||
Function::Version(p, v, c) => evaluate_version(state, p, v, *c),
|
||||
Function::Version(p, v, c) => evaluate_version(state, p, v, *c, get_version),
|
||||
Function::ProductVersion(p, v, c) => {
|
||||
evaluate_version(state, p, v, *c, |_, p| get_product_version(p))
|
||||
}
|
||||
};
|
||||
|
||||
if self.is_slow() {
|
||||
@@ -902,4 +917,44 @@ mod tests {
|
||||
|
||||
assert!(function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_version_eval_should_read_executable_file_version() {
|
||||
let function = Function::Version(
|
||||
"loot_api.dll".into(),
|
||||
"0.13.8.0".into(),
|
||||
ComparisonOperator::Equal,
|
||||
);
|
||||
let state = state("tests/loot_api_win32");
|
||||
|
||||
assert!(function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_product_version_eval_should_read_executable_product_version() {
|
||||
let function =
|
||||
Function::Version("7za.exe".into(), "18.05".into(), ComparisonOperator::Equal);
|
||||
let state = state("tests/7z");
|
||||
|
||||
assert!(function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_product_version_should_return_ok_none_if_the_path_does_not_exist() {
|
||||
assert!(get_product_version(Path::new("missing")).unwrap().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_product_version_should_return_ok_some_if_the_path_is_an_executable() {
|
||||
let version = get_product_version(Path::new("tests/7z/7za.exe"))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(Version::from("18.05"), version);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_product_version_should_error_if_the_path_is_not_an_executable() {
|
||||
assert!(get_product_version(Path::new("Cargo.toml")).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ pub enum Function {
|
||||
ManyActive(Regex),
|
||||
Checksum(PathBuf, u32),
|
||||
Version(PathBuf, String, ComparisonOperator),
|
||||
ProductVersion(PathBuf, String, ComparisonOperator),
|
||||
}
|
||||
|
||||
impl fmt::Display for Function {
|
||||
@@ -57,6 +58,9 @@ impl fmt::Display for Function {
|
||||
ManyActive(r) => write!(f, "many_active(\"{}\")", r),
|
||||
Checksum(p, c) => write!(f, "checksum(\"{}\", {:02X?})", p.display(), c),
|
||||
Version(p, v, c) => write!(f, "version(\"{}\", \"{}\", {})", p.display(), v, c),
|
||||
ProductVersion(p, v, c) => {
|
||||
write!(f, "product_version(\"{}\", \"{}\", {})", p.display(), v, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,6 +85,9 @@ impl PartialEq for Function {
|
||||
(Version(p1, v1, c1), Version(p2, v2, c2)) => {
|
||||
c1 == c2 && eq(&v1, &v2) && eq(&p1.to_string_lossy(), &p2.to_string_lossy())
|
||||
}
|
||||
(ProductVersion(p1, v1, c1), ProductVersion(p2, v2, c2)) => {
|
||||
c1 == c2 && eq(&v1, &v2) && eq(&p1.to_string_lossy(), &p2.to_string_lossy())
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -121,6 +128,11 @@ impl Hash for Function {
|
||||
v.to_lowercase().hash(state);
|
||||
c.hash(state);
|
||||
}
|
||||
ProductVersion(p, v, c) => {
|
||||
p.to_string_lossy().to_lowercase().hash(state);
|
||||
v.to_lowercase().hash(state);
|
||||
c.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
discriminant(self).hash(state);
|
||||
@@ -203,6 +215,20 @@ mod tests {
|
||||
&format!("{}", function)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_fmt_for_product_version_should_format_correctly() {
|
||||
let function = Function::ProductVersion(
|
||||
"../TESV.exe".into(),
|
||||
"1.2a".into(),
|
||||
ComparisonOperator::Equal,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
"product_version(\"../TESV.exe\", \"1.2a\", ==)",
|
||||
&format!("{}", function)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod eq {
|
||||
@@ -419,6 +445,39 @@ mod tests {
|
||||
Function::Version("blank.esm".into(), "a".into(), ComparisonOperator::Equal)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_eq_for_product_version_should_check_pathbuf_version_and_comparator() {
|
||||
assert_eq!(
|
||||
Function::ProductVersion("Blank.esm".into(), "1".into(), ComparisonOperator::Equal),
|
||||
Function::ProductVersion("Blank.esm".into(), "1".into(), ComparisonOperator::Equal)
|
||||
);
|
||||
|
||||
assert_ne!(
|
||||
Function::ProductVersion("Blank.esm".into(), "1".into(), ComparisonOperator::Equal),
|
||||
Function::ProductVersion("Blank.esp".into(), "1".into(), ComparisonOperator::Equal)
|
||||
);
|
||||
assert_ne!(
|
||||
Function::ProductVersion("Blank.esm".into(), "1".into(), ComparisonOperator::Equal),
|
||||
Function::ProductVersion("Blank.esm".into(), "2".into(), ComparisonOperator::Equal)
|
||||
);
|
||||
assert_ne!(
|
||||
Function::ProductVersion("Blank.esm".into(), "1".into(), ComparisonOperator::Equal),
|
||||
Function::ProductVersion(
|
||||
"Blank.esm".into(),
|
||||
"1".into(),
|
||||
ComparisonOperator::NotEqual
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_eq_for_product_version_should_be_case_insensitive_on_pathbuf_and_version() {
|
||||
assert_eq!(
|
||||
Function::ProductVersion("Blank.esm".into(), "A".into(), ComparisonOperator::Equal),
|
||||
Function::ProductVersion("blank.esm".into(), "a".into(), ComparisonOperator::Equal)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod hash {
|
||||
@@ -658,5 +717,61 @@ mod tests {
|
||||
|
||||
assert_eq!(hash(function1), hash(function2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_hash_product_version_should_hash_pathbuf_and_version_and_comparator() {
|
||||
let function1 = Function::ProductVersion(
|
||||
"Blank.esm".into(),
|
||||
"1.2a".into(),
|
||||
ComparisonOperator::Equal,
|
||||
);
|
||||
let function2 = Function::ProductVersion(
|
||||
"Blank.esm".into(),
|
||||
"1.2a".into(),
|
||||
ComparisonOperator::Equal,
|
||||
);
|
||||
|
||||
assert_eq!(hash(function1), hash(function2));
|
||||
|
||||
let function1 =
|
||||
Function::ProductVersion("Blank.esm".into(), "1".into(), ComparisonOperator::Equal);
|
||||
let function2 =
|
||||
Function::ProductVersion("Blank.esp".into(), "1".into(), ComparisonOperator::Equal);
|
||||
|
||||
assert_ne!(hash(function1), hash(function2));
|
||||
|
||||
let function1 =
|
||||
Function::ProductVersion("Blank.esm".into(), "1".into(), ComparisonOperator::Equal);
|
||||
let function2 =
|
||||
Function::ProductVersion("Blank.esm".into(), "2".into(), ComparisonOperator::Equal);
|
||||
|
||||
assert_ne!(hash(function1), hash(function2));
|
||||
|
||||
let function1 =
|
||||
Function::ProductVersion("Blank.esm".into(), "1".into(), ComparisonOperator::Equal);
|
||||
let function2 = Function::ProductVersion(
|
||||
"Blank.esm".into(),
|
||||
"1".into(),
|
||||
ComparisonOperator::NotEqual,
|
||||
);
|
||||
|
||||
assert_ne!(hash(function1), hash(function2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_hash_product_version_should_be_case_insensitive() {
|
||||
let function1 = Function::ProductVersion(
|
||||
"Blank.esm".into(),
|
||||
"1.2a".into(),
|
||||
ComparisonOperator::Equal,
|
||||
);
|
||||
let function2 = Function::ProductVersion(
|
||||
"Blank.esm".into(),
|
||||
"1.2A".into(),
|
||||
ComparisonOperator::Equal,
|
||||
);
|
||||
|
||||
assert_eq!(hash(function1), hash(function2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-1
@@ -228,6 +228,15 @@ impl Function {
|
||||
Function::Version(path, version, comparator)
|
||||
}
|
||||
} |
|
||||
delimited!(
|
||||
fix_error!(ParsingError, tag!("product_version(")),
|
||||
call!(parse_version_args),
|
||||
fix_error!(ParsingError, tag!(")"))
|
||||
) => {
|
||||
|(path, version, comparator)| {
|
||||
Function::ProductVersion(path, version, comparator)
|
||||
}
|
||||
} |
|
||||
delimited!(
|
||||
fix_error!(ParsingError, tag!("checksum(")),
|
||||
call!(parse_checksum_args),
|
||||
@@ -419,7 +428,7 @@ mod tests {
|
||||
assert_eq!("1.2", version);
|
||||
assert_eq!(ComparisonOperator::Equal, comparator);
|
||||
}
|
||||
_ => panic!("Expected a checksum function"),
|
||||
_ => panic!("Expected a version function"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,4 +436,27 @@ mod tests {
|
||||
fn function_parse_should_error_if_the_version_path_is_outside_the_game_directory() {
|
||||
assert!(Function::parse("version(\"../../Cargo.toml\", \"1.2\", ==)".into()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_parse_should_parse_a_product_version_equals_function() {
|
||||
let result = Function::parse("product_version(\"Cargo.toml\", \"1.2\", ==)".into())
|
||||
.unwrap()
|
||||
.1;
|
||||
|
||||
match result {
|
||||
Function::ProductVersion(path, version, comparator) => {
|
||||
assert_eq!(Path::new("Cargo.toml"), path);
|
||||
assert_eq!("1.2", version);
|
||||
assert_eq!(ComparisonOperator::Equal, comparator);
|
||||
}
|
||||
_ => panic!("Expected a product version function"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_parse_should_error_if_the_product_version_path_is_outside_the_game_directory() {
|
||||
assert!(
|
||||
Function::parse("product_version(\"../../Cargo.toml\", \"1.2\", ==)".into()).is_err()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user