Trim .ghost extension from ghosted plugin before regex matching

If an installed plugin has a .ghost extension, remove it before trying
to match the filename against the given regex. This makes functions that
take paths and functions that take regexes behave the same way for
ghosted plugins.
This commit is contained in:
Oliver Hamlet
2021-04-08 21:03:27 +01:00
parent d2db8ed642
commit a8aaf3c9e5
2 changed files with 56 additions and 7 deletions
+45 -7
View File
@@ -6,19 +6,19 @@ use std::path::Path;
use regex::Regex;
use super::path::{has_plugin_file_extension, resolve_path};
use super::path::{has_plugin_file_extension, normalise_file_name, resolve_path};
use super::version::Version;
use super::{ComparisonOperator, Function};
use crate::{Error, State};
use crate::{Error, GameType, State};
fn evaluate_file_path(state: &State, file_path: &Path) -> Result<bool, Error> {
Ok(resolve_path(state, file_path).exists())
}
fn is_match(regex: &Regex, file_name: &OsStr) -> bool {
fn is_match(game_type: GameType, regex: &Regex, file_name: &OsStr) -> bool {
file_name
.to_str()
.map(|s| regex.is_match(s))
.map(|s| regex.is_match(normalise_file_name(game_type, s)))
.unwrap_or(false)
}
@@ -30,7 +30,7 @@ fn evaluate_file_regex(state: &State, parent_path: &Path, regex: &Regex) -> Resu
for entry in dir_iterator {
let entry = entry.map_err(|e| Error::IoError(parent_path.to_path_buf(), e))?;
if is_match(regex, &entry.file_name()) {
if is_match(state.game_type, regex, &entry.file_name()) {
return Ok(true);
}
}
@@ -47,7 +47,7 @@ fn evaluate_many(state: &State, parent_path: &Path, regex: &Regex) -> Result<boo
let mut found_one = false;
for entry in dir_iterator {
let entry = entry.map_err(|e| Error::IoError(parent_path.to_path_buf(), e))?;
if is_match(regex, &entry.file_name()) {
if is_match(state.game_type, regex, &entry.file_name()) {
if found_one {
return Ok(true);
} else {
@@ -71,7 +71,6 @@ fn evaluate_active_regex(state: &State, regex: &Regex) -> Result<bool, Error> {
}
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 {
@@ -426,6 +425,23 @@ mod tests {
assert!(function.eval(&state).unwrap());
}
#[test]
fn function_file_regex_eval_should_trim_ghost_plugin_extension_before_matching_against_regex() {
let tmp_dir = tempdir().unwrap();
let data_path = tmp_dir.path().join("Data");
let state = state(data_path);
copy(
Path::new("tests/testing-plugins/Oblivion/Data/Blank.esm"),
&state.data_path.join("Blank.esm.ghost"),
)
.unwrap();
let function = Function::FileRegex(PathBuf::from("."), regex("^Blank\\.esm$"));
assert!(function.eval(&state).unwrap());
}
#[test]
fn function_active_path_eval_should_be_true_if_the_path_is_an_active_plugin() {
let function = Function::ActivePath(PathBuf::from("Blank.esp"));
@@ -536,6 +552,28 @@ mod tests {
assert!(function.eval(&state).unwrap());
}
#[test]
fn function_many_eval_should_trim_ghost_plugin_extension_before_matching_against_regex() {
let tmp_dir = tempdir().unwrap();
let data_path = tmp_dir.path().join("Data");
let state = state(data_path);
copy(
Path::new("tests/testing-plugins/Oblivion/Data/Blank.esm"),
&state.data_path.join("Blank.esm.ghost"),
)
.unwrap();
copy(
Path::new("tests/testing-plugins/Oblivion/Data/Blank.esp"),
&state.data_path.join("Blank.esp.ghost"),
)
.unwrap();
let function = Function::Many(PathBuf::from("."), regex("^Blank\\.es(m|p)$"));
assert!(function.eval(&state).unwrap());
}
#[test]
fn function_many_active_eval_should_be_true_if_the_regex_matches_more_than_one_active_plugin() {
let function = Function::ManyActive(regex("Blank.*"));
+11
View File
@@ -45,6 +45,17 @@ fn add_ghost_extension(path: PathBuf) -> PathBuf {
}
}
pub fn normalise_file_name<'a>(game_type: GameType, name: &'a str) -> &'a str {
if name.ends_with(GHOST_EXTENSION_WITH_PERIOD) {
let stem = &name[..name.len() - GHOST_EXTENSION_WITH_PERIOD.len()];
if has_unghosted_plugin_file_extension(game_type, Path::new(stem)) {
return stem;
}
}
name
}
pub fn resolve_path(state: &State, path: &Path) -> PathBuf {
if path == Path::new("LOOT") {
state.loot_path.clone()