mirror of
https://github.com/loot/loot-condition-interpreter.git
synced 2026-07-27 14:16:09 -07:00
Add support for an is_master(file path) function
This commit is contained in:
@@ -7,6 +7,7 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
crc = "1.0.0"
|
||||
esplugin = "3.0.0"
|
||||
nom = "5.0.0"
|
||||
pelite = "0.7.0"
|
||||
regex = "1.0.5"
|
||||
|
||||
@@ -99,6 +99,30 @@ fn evaluate_active_regex(state: &State, regex: &Regex) -> Result<bool, Error> {
|
||||
Ok(state.active_plugins.iter().any(|p| regex.is_match(p)))
|
||||
}
|
||||
|
||||
fn evaluate_is_master(state: &State, file_path: &Path) -> Result<bool, Error> {
|
||||
use crate::GameType;
|
||||
use esplugin::GameId;
|
||||
|
||||
let game_id = match state.game_type {
|
||||
GameType::Morrowind => GameId::Morrowind,
|
||||
GameType::Oblivion => GameId::Oblivion,
|
||||
GameType::Skyrim => GameId::Skyrim,
|
||||
GameType::SkyrimSE | GameType::SkyrimVR => GameId::SkyrimSE,
|
||||
GameType::Fallout3 => GameId::Fallout3,
|
||||
GameType::FalloutNV => GameId::FalloutNV,
|
||||
GameType::Fallout4 | GameType::Fallout4VR => GameId::Fallout4,
|
||||
};
|
||||
|
||||
let path = resolve_path(state, file_path);
|
||||
|
||||
let mut plugin = esplugin::Plugin::new(game_id, &path);
|
||||
|
||||
plugin
|
||||
.parse_file(true)
|
||||
.map(|_| plugin.is_master_file())
|
||||
.or(Ok(false))
|
||||
}
|
||||
|
||||
fn evaluate_many_active(state: &State, regex: &Regex) -> Result<bool, Error> {
|
||||
let mut found_one = false;
|
||||
for active_plugin in &state.active_plugins {
|
||||
@@ -231,6 +255,7 @@ impl Function {
|
||||
Function::FileRegex(p, r) => evaluate_file_regex(state, p, r),
|
||||
Function::ActivePath(p) => evaluate_active_path(state, p),
|
||||
Function::ActiveRegex(r) => evaluate_active_regex(state, r),
|
||||
Function::IsMaster(p) => evaluate_is_master(state, p),
|
||||
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),
|
||||
@@ -464,6 +489,38 @@ mod tests {
|
||||
assert!(!function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_is_master_eval_should_be_true_if_the_path_is_a_master_plugin() {
|
||||
let function = Function::IsMaster(PathBuf::from("Blank.esm"));
|
||||
let state = state("tests/testing-plugins/Oblivion/Data");
|
||||
|
||||
assert!(function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_is_master_eval_should_be_false_if_the_path_does_not_exist() {
|
||||
let function = Function::IsMaster(PathBuf::from("missing.esp"));
|
||||
let state = state("tests/testing-plugins/Oblivion/Data");
|
||||
|
||||
assert!(!function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_is_master_eval_should_be_false_if_the_path_is_not_a_plugin() {
|
||||
let function = Function::IsMaster(PathBuf::from("Cargo.toml"));
|
||||
let state = state(".");
|
||||
|
||||
assert!(!function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_is_master_eval_should_be_false_if_the_path_is_a_non_master_plugin() {
|
||||
let function = Function::IsMaster(PathBuf::from("Blank.esp"));
|
||||
let state = state("tests/testing-plugins/Oblivion/Data");
|
||||
|
||||
assert!(!function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_many_eval_should_be_false_if_no_directory_entries_match() {
|
||||
let function = Function::Many(PathBuf::from("."), regex("missing"));
|
||||
|
||||
@@ -39,6 +39,7 @@ pub enum Function {
|
||||
FileRegex(PathBuf, Regex),
|
||||
ActivePath(PathBuf),
|
||||
ActiveRegex(Regex),
|
||||
IsMaster(PathBuf),
|
||||
Many(PathBuf, Regex),
|
||||
ManyActive(Regex),
|
||||
Checksum(PathBuf, u32),
|
||||
@@ -54,6 +55,7 @@ impl fmt::Display for Function {
|
||||
FileRegex(p, r) => write!(f, "file(\"{}/{}\")", p.display(), r),
|
||||
ActivePath(p) => write!(f, "active(\"{}\")", p.display()),
|
||||
ActiveRegex(r) => write!(f, "active(\"{}\")", r),
|
||||
IsMaster(p) => write!(f, "is_master(\"{}\")", p.display()),
|
||||
Many(p, r) => write!(f, "many(\"{}/{}\")", p.display(), r),
|
||||
ManyActive(r) => write!(f, "many_active(\"{}\")", r),
|
||||
Checksum(p, c) => write!(f, "checksum(\"{}\", {:02X?})", p.display(), c),
|
||||
@@ -75,6 +77,7 @@ impl PartialEq for Function {
|
||||
}
|
||||
(ActivePath(p1), ActivePath(p2)) => eq(&p1.to_string_lossy(), &p2.to_string_lossy()),
|
||||
(ActiveRegex(r1), ActiveRegex(r2)) => eq(r1.as_str(), r2.as_str()),
|
||||
(IsMaster(p1), IsMaster(p2)) => eq(&p1.to_string_lossy(), &p2.to_string_lossy()),
|
||||
(Many(p1, r1), Many(p2, r2)) => {
|
||||
eq(r1.as_str(), r2.as_str()) && eq(&p1.to_string_lossy(), &p2.to_string_lossy())
|
||||
}
|
||||
@@ -112,6 +115,9 @@ impl Hash for Function {
|
||||
ActiveRegex(r) => {
|
||||
r.as_str().to_lowercase().hash(state);
|
||||
}
|
||||
IsMaster(p) => {
|
||||
p.to_string_lossy().to_lowercase().hash(state);
|
||||
}
|
||||
Many(p, r) => {
|
||||
p.to_string_lossy().to_lowercase().hash(state);
|
||||
r.as_str().to_lowercase().hash(state);
|
||||
@@ -178,6 +184,13 @@ mod tests {
|
||||
assert_eq!("active(\"Blank.*\")", &format!("{}", function));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_fmt_for_is_master_should_format_correctly() {
|
||||
let function = Function::IsMaster("Blank.esm".into());
|
||||
|
||||
assert_eq!("is_master(\"Blank.esm\")", &format!("{}", function));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_fmt_for_many_should_format_correctly() {
|
||||
let function = Function::Many("subdir".into(), regex("Blank.*"));
|
||||
@@ -330,6 +343,43 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_eq_for_is_master_should_check_pathbuf() {
|
||||
assert_eq!(
|
||||
Function::IsMaster("Blank.esm".into()),
|
||||
Function::IsMaster("Blank.esm".into())
|
||||
);
|
||||
|
||||
assert_ne!(
|
||||
Function::IsMaster("Blank.esp".into()),
|
||||
Function::IsMaster("Blank.esm".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_eq_for_is_master_should_be_case_insensitive_on_pathbuf() {
|
||||
assert_eq!(
|
||||
Function::IsMaster("Blank.esm".into()),
|
||||
Function::IsMaster("blank.esm".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_eq_for_is_master_should_not_be_equal_to_file_path_with_same_pathbuf() {
|
||||
assert_ne!(
|
||||
Function::IsMaster("Blank.esm".into()),
|
||||
Function::FilePath("Blank.esm".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_eq_for_active_path_should_not_be_equal_to_active_path_with_same_pathbuf() {
|
||||
assert_ne!(
|
||||
Function::IsMaster("Blank.esm".into()),
|
||||
Function::ActivePath("Blank.esm".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_eq_for_many_should_check_pathbuf_and_regex() {
|
||||
assert_eq!(
|
||||
@@ -588,6 +638,43 @@ mod tests {
|
||||
assert_eq!(hash(function1), hash(function2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_hash_is_master_should_hash_pathbuf() {
|
||||
let function1 = Function::IsMaster("Blank.esm".into());
|
||||
let function2 = Function::IsMaster("Blank.esm".into());
|
||||
|
||||
assert_eq!(hash(function1), hash(function2));
|
||||
|
||||
let function1 = Function::IsMaster("Blank.esm".into());
|
||||
let function2 = Function::IsMaster("Blank.esp".into());
|
||||
|
||||
assert_ne!(hash(function1), hash(function2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_hash_is_master_should_be_case_insensitive() {
|
||||
let function1 = Function::IsMaster("Blank.esm".into());
|
||||
let function2 = Function::IsMaster("blank.esm".into());
|
||||
|
||||
assert_eq!(hash(function1), hash(function2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_hash_file_path_and_is_master_should_not_have_equal_hashes() {
|
||||
let function1 = Function::FilePath("Blank.esm".into());
|
||||
let function2 = Function::IsMaster("Blank.esm".into());
|
||||
|
||||
assert_ne!(hash(function1), hash(function2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_hash_active_path_and_is_master_should_not_have_equal_hashes() {
|
||||
let function1 = Function::FilePath("Blank.esm".into());
|
||||
let function2 = Function::IsMaster("Blank.esm".into());
|
||||
|
||||
assert_ne!(hash(function1), hash(function2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_hash_many_should_hash_pathbuf_and_regex() {
|
||||
let function1 = Function::Many("subdir".into(), regex(".*"));
|
||||
|
||||
@@ -186,6 +186,14 @@ impl Function {
|
||||
),
|
||||
Function::ActiveRegex,
|
||||
),
|
||||
map(
|
||||
delimited(
|
||||
map_err(tag("is_master(\"")),
|
||||
parse_non_regex_path,
|
||||
map_err(tag("\")")),
|
||||
),
|
||||
Function::IsMaster,
|
||||
),
|
||||
map(
|
||||
delimited(
|
||||
map_err(tag("many(\"")),
|
||||
@@ -335,6 +343,24 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_parse_should_parse_an_is_master_function() {
|
||||
let output = Function::parse("is_master(\"Blank.esm\")".into()).unwrap();
|
||||
|
||||
assert!(output.0.is_empty());
|
||||
match output.1 {
|
||||
Function::IsMaster(f) => assert_eq!(Path::new("Blank.esm"), f),
|
||||
_ => panic!("Expected an is master function"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_parse_should_error_if_the_is_master_path_is_outside_the_game_directory() {
|
||||
// Trying to check if a path that isn't a plugin in the data folder is
|
||||
// active is pointless, but it's not worth having a more specific check.
|
||||
assert!(Function::parse("is_master(\"../../Blank.esm\")".into()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_parse_should_parse_a_many_function_with_no_parent_path() {
|
||||
let output = Function::parse("many(\"Cargo.*\")".into()).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user