mirror of
https://github.com/loot/loot-condition-interpreter.git
synced 2026-07-27 14:16:09 -07:00
Add file_size() function
It evaluates to false if the file doesn't exist.
This commit is contained in:
@@ -64,6 +64,12 @@ fn evaluate_file_regex(state: &State, parent_path: &Path, regex: &Regex) -> Resu
|
||||
)
|
||||
}
|
||||
|
||||
fn evaluate_file_size(state: &State, path: &Path, size: u64) -> Result<bool, Error> {
|
||||
std::fs::metadata(resolve_path(state, path))
|
||||
.map(|m| m.len() == size)
|
||||
.or(Ok(false))
|
||||
}
|
||||
|
||||
fn evaluate_readable(state: &State, path: &Path) -> Result<bool, Error> {
|
||||
if path.is_dir() {
|
||||
Ok(read_dir(resolve_path(state, path)).is_ok())
|
||||
@@ -283,6 +289,7 @@ impl Function {
|
||||
let result = match self {
|
||||
Function::FilePath(f) => evaluate_file_path(state, f),
|
||||
Function::FileRegex(p, r) => evaluate_file_regex(state, p, r),
|
||||
Function::FileSize(p, s) => evaluate_file_size(state, p, *s),
|
||||
Function::Readable(p) => evaluate_readable(state, p),
|
||||
Function::IsExecutable(p) => evaluate_is_executable(state, p),
|
||||
Function::ActivePath(p) => evaluate_active_path(state, p),
|
||||
@@ -502,6 +509,79 @@ mod tests {
|
||||
assert!(function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_file_size_eval_should_return_false_if_file_does_not_exist() {
|
||||
let function = Function::FileSize("missing.esp".into(), 55);
|
||||
let state = state_with_data(
|
||||
"./src",
|
||||
vec!["./tests/testing-plugins/Oblivion/Data"],
|
||||
&[],
|
||||
&[],
|
||||
);
|
||||
|
||||
assert!(!function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_file_size_eval_should_return_false_if_file_size_is_different() {
|
||||
let function = Function::FileSize("Blank.esp".into(), 10);
|
||||
let state = state_with_data(
|
||||
"./src",
|
||||
vec!["./tests/testing-plugins/Oblivion/Data"],
|
||||
&[],
|
||||
&[],
|
||||
);
|
||||
|
||||
assert!(!function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_file_size_eval_should_return_true_if_file_size_is_equal() {
|
||||
let function = Function::FileSize("Blank.esp".into(), 55);
|
||||
let state = state_with_data(
|
||||
"./src",
|
||||
vec!["./tests/testing-plugins/Oblivion/Data"],
|
||||
&[],
|
||||
&[],
|
||||
);
|
||||
|
||||
assert!(function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_file_size_eval_should_return_true_if_given_a_plugin_that_is_ghosted() {
|
||||
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.esp"),
|
||||
state.data_path.join("Blank.esp.ghost"),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let function = Function::FileSize(PathBuf::from("Blank.esp"), 55);
|
||||
|
||||
assert!(function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_file_size_eval_should_not_check_for_ghosted_non_plugin_file() {
|
||||
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.bsa"),
|
||||
state.data_path.join("Blank.bsa.ghost"),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let function = Function::FileSize(PathBuf::from("Blank.bsa"), 736);
|
||||
|
||||
assert!(!function.eval(&state).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_readable_eval_should_be_true_for_a_file_that_can_be_opened_as_read_only() {
|
||||
let function = Function::Readable(PathBuf::from("Cargo.toml"));
|
||||
|
||||
+72
-2
@@ -39,6 +39,7 @@ impl fmt::Display for ComparisonOperator {
|
||||
pub enum Function {
|
||||
FilePath(PathBuf),
|
||||
FileRegex(PathBuf, Regex),
|
||||
FileSize(PathBuf, u64),
|
||||
Readable(PathBuf),
|
||||
IsExecutable(PathBuf),
|
||||
ActivePath(PathBuf),
|
||||
@@ -57,6 +58,7 @@ impl fmt::Display for Function {
|
||||
match self {
|
||||
FilePath(p) => write!(f, "file(\"{}\")", p.display()),
|
||||
FileRegex(p, r) => write!(f, "file(\"{}/{}\")", p.display(), r),
|
||||
FileSize(p, s) => write!(f, "file_size(\"{}\", {})", p.display(), s),
|
||||
Readable(p) => write!(f, "readable(\"{}\")", p.display()),
|
||||
IsExecutable(p) => write!(f, "is_executable(\"{}\")", p.display()),
|
||||
ActivePath(p) => write!(f, "active(\"{}\")", p.display()),
|
||||
@@ -81,6 +83,9 @@ impl PartialEq for Function {
|
||||
(FileRegex(p1, r1), FileRegex(p2, r2)) => {
|
||||
eq(r1.as_str(), r2.as_str()) && eq(&p1.to_string_lossy(), &p2.to_string_lossy())
|
||||
}
|
||||
(FileSize(p1, s1), FileSize(p2, s2)) => {
|
||||
s1 == s2 && eq(&p1.to_string_lossy(), &p2.to_string_lossy())
|
||||
}
|
||||
(Readable(p1), Readable(p2)) => eq(&p1.to_string_lossy(), &p2.to_string_lossy()),
|
||||
(IsExecutable(p1), IsExecutable(p2)) => {
|
||||
eq(&p1.to_string_lossy(), &p2.to_string_lossy())
|
||||
@@ -119,6 +124,10 @@ impl Hash for Function {
|
||||
p.to_string_lossy().to_lowercase().hash(state);
|
||||
r.as_str().to_lowercase().hash(state);
|
||||
}
|
||||
FileSize(p, s) => {
|
||||
p.to_string_lossy().to_lowercase().hash(state);
|
||||
s.hash(state);
|
||||
}
|
||||
Readable(p) => {
|
||||
p.to_string_lossy().to_lowercase().hash(state);
|
||||
}
|
||||
@@ -186,6 +195,16 @@ mod tests {
|
||||
assert_eq!("file(\"subdir/Blank.*\")", &format!("{}", function));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_fmt_for_file_size_should_format_correctly() {
|
||||
let function = Function::FileSize("subdir/Blank.esm".into(), 12345678);
|
||||
|
||||
assert_eq!(
|
||||
"file_size(\"subdir/Blank.esm\", 12345678)",
|
||||
&format!("{}", function)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_fmt_for_readable_should_format_correctly() {
|
||||
let function = Function::Readable("subdir/Blank.esm".into());
|
||||
@@ -326,6 +345,31 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_eq_for_file_size_should_check_pathbuf_and_size() {
|
||||
assert_eq!(
|
||||
Function::FileSize("subdir".into(), 1),
|
||||
Function::FileSize("subdir".into(), 1)
|
||||
);
|
||||
|
||||
assert_ne!(
|
||||
Function::FileSize("subdir".into(), 1),
|
||||
Function::FileSize("other".into(), 1)
|
||||
);
|
||||
assert_ne!(
|
||||
Function::FileSize("subdir".into(), 1),
|
||||
Function::FileSize("subdir".into(), 2)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_eq_for_file_size_should_be_case_insensitive_on_pathbuf() {
|
||||
assert_eq!(
|
||||
Function::FileSize("subdir".into(), 1),
|
||||
Function::FileSize("Subdir".into(), 1)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_eq_for_readable_should_check_pathbuf() {
|
||||
assert_eq!(
|
||||
@@ -512,8 +556,8 @@ mod tests {
|
||||
#[test]
|
||||
fn function_eq_for_many_should_be_case_insensitive_on_pathbuf_and_regex() {
|
||||
assert_eq!(
|
||||
Function::FileRegex("subdir".into(), regex("blank.*")),
|
||||
Function::FileRegex("Subdir".into(), regex("Blank.*"))
|
||||
Function::Many("subdir".into(), regex("blank.*")),
|
||||
Function::Many("Subdir".into(), regex("Blank.*"))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -700,6 +744,32 @@ mod tests {
|
||||
assert_eq!(hash(function1), hash(function2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_hash_file_size_should_hash_pathbuf_and_size() {
|
||||
let function1 = Function::FileSize("subdir".into(), 1);
|
||||
let function2 = Function::FileSize("subdir".into(), 1);
|
||||
|
||||
assert_eq!(hash(function1), hash(function2));
|
||||
|
||||
let function1 = Function::FileSize("subdir".into(), 1);
|
||||
let function2 = Function::FileSize("other".into(), 1);
|
||||
|
||||
assert_ne!(hash(function1), hash(function2));
|
||||
|
||||
let function1 = Function::FileSize("subdir".into(), 1);
|
||||
let function2 = Function::FileSize("subdir".into(), 2);
|
||||
|
||||
assert_ne!(hash(function1), hash(function2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_hash_file_size_should_be_case_insensitive() {
|
||||
let function1 = Function::FileSize("Subdir".into(), 1);
|
||||
let function2 = Function::FileSize("subdir".into(), 1);
|
||||
|
||||
assert_eq!(hash(function1), hash(function2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_hash_readable_should_hash_pathbuf() {
|
||||
let function1 = Function::Readable("Blank.esm".into());
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::str;
|
||||
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::{is_not, tag};
|
||||
use nom::character::complete::digit1;
|
||||
use nom::character::complete::hex_digit1;
|
||||
use nom::combinator::{map, map_parser, value};
|
||||
use nom::sequence::delimited;
|
||||
@@ -65,6 +66,28 @@ fn parse_path(input: &str) -> IResult<&str, PathBuf> {
|
||||
.parse(input)
|
||||
}
|
||||
|
||||
fn parse_size(input: &str) -> ParsingResult<u64> {
|
||||
str::parse(input)
|
||||
.map(|c| ("", c))
|
||||
.map_err(|e| Err::Failure(ParsingErrorKind::from(e).at(input)))
|
||||
}
|
||||
|
||||
fn parse_file_size_args(input: &str) -> ParsingResult<(PathBuf, u64)> {
|
||||
let mut parser = (
|
||||
map_err(parse_path),
|
||||
map_err(whitespace(tag(","))),
|
||||
map_parser(digit1, parse_size),
|
||||
);
|
||||
|
||||
let (remaining_input, (path, _, size)) = parser.parse(input)?;
|
||||
|
||||
if is_in_game_path(&path) {
|
||||
Ok((remaining_input, (path, size)))
|
||||
} else {
|
||||
Err(not_in_game_directory(input, path))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_version_args(input: &str) -> ParsingResult<(PathBuf, String, ComparisonOperator)> {
|
||||
let version_parser = map(
|
||||
delimited(tag("\""), is_not("\""), tag("\"")),
|
||||
@@ -173,6 +196,14 @@ impl Function {
|
||||
),
|
||||
|(path, regex)| Function::FileRegex(path, regex),
|
||||
),
|
||||
map(
|
||||
delimited(
|
||||
map_err(tag("file_size(")),
|
||||
parse_file_size_args,
|
||||
map_err(tag(")")),
|
||||
),
|
||||
|(path, size)| Function::FileSize(path, size),
|
||||
),
|
||||
map(
|
||||
delimited(
|
||||
map_err(tag("readable(\"")),
|
||||
@@ -332,6 +363,25 @@ mod tests {
|
||||
assert!(Function::parse("file(\"../../Cargo.*\")").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_parse_should_parse_a_file_size_function() {
|
||||
let output = Function::parse("file_size(\"Cargo.toml\", 1234)").unwrap();
|
||||
|
||||
assert!(output.0.is_empty());
|
||||
match output.1 {
|
||||
Function::FileSize(f, s) => {
|
||||
assert_eq!(Path::new("Cargo.toml"), f);
|
||||
assert_eq!(1234, s);
|
||||
}
|
||||
_ => panic!("Expected a file size function"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_parse_should_error_if_the_file_size_is_outside_the_game_directory() {
|
||||
assert!(Function::parse("file_size(\"../../Cargo.toml\", 1234)").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_parse_should_parse_a_readable_function() {
|
||||
let output = Function::parse("readable(\"Cargo.toml\")").unwrap();
|
||||
|
||||
Reference in New Issue
Block a user