mirror of
https://github.com/loot/loot-condition-interpreter.git
synced 2026-07-27 14:16:09 -07:00
Implement file path function evaluation
This commit is contained in:
@@ -2,3 +2,4 @@
|
|||||||
/target/
|
/target/
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
/testing-plugins
|
||||||
|
|||||||
@@ -6,3 +6,6 @@ authors = ["Oliver Hamlet <oliver.hamlet@gmail.com>"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
nom = "4.0.0"
|
nom = "4.0.0"
|
||||||
regex = "1.0.5"
|
regex = "1.0.5"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = "3.0.0"
|
||||||
|
|||||||
@@ -19,3 +19,6 @@ Goals:
|
|||||||
|
|
||||||
Currently only condition parsing is complete. Evaluation is partially done, the
|
Currently only condition parsing is complete. Evaluation is partially done, the
|
||||||
rest hasn't yet been started.
|
rest hasn't yet been started.
|
||||||
|
|
||||||
|
The tests need the [testing-plugins](https://github.com/WrinklyNinja/testing-plugins)
|
||||||
|
directory to be present in the repo root.
|
||||||
|
|||||||
+134
-15
@@ -1,12 +1,77 @@
|
|||||||
use ::Error;
|
use std::ffi::OsStr;
|
||||||
|
use std::path::{Component, Path, PathBuf};
|
||||||
|
|
||||||
use super::Function;
|
use super::Function;
|
||||||
|
use Error;
|
||||||
|
use State;
|
||||||
|
|
||||||
|
fn has_plugin_file_extension(path: &Path, state: &State) -> bool {
|
||||||
|
match path.extension().and_then(OsStr::to_str) {
|
||||||
|
Some("esp") | Some("esm") => true,
|
||||||
|
Some("esl") if state.game_type.supports_light_plugins() => true,
|
||||||
|
Some("ghost") => path
|
||||||
|
.file_stem()
|
||||||
|
.map(|s| has_plugin_file_extension(Path::new(s), state))
|
||||||
|
.unwrap_or(false),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_extension(path: &Path, extension: &str) -> PathBuf {
|
||||||
|
match path.extension() {
|
||||||
|
Some(e) => {
|
||||||
|
let mut new_extension = e.to_os_string();
|
||||||
|
new_extension.push(format!(".{}", extension));
|
||||||
|
path.with_extension(&new_extension)
|
||||||
|
}
|
||||||
|
None => path.with_extension(extension),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn equals(path: &Path, test: &str) -> bool {
|
||||||
|
path.to_str().map(|s| s == test).unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_in_game_path(path: &Path) -> bool {
|
||||||
|
let mut previous_component = Component::CurDir;
|
||||||
|
for component in path.components() {
|
||||||
|
match (component, previous_component) {
|
||||||
|
(Component::Prefix(_), _) => return false,
|
||||||
|
(Component::RootDir, _) => return false,
|
||||||
|
(Component::ParentDir, Component::ParentDir) => return false,
|
||||||
|
(Component::CurDir, _) => continue,
|
||||||
|
_ => previous_component = component,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate_file_path(state: &State, file_path: &Path) -> Result<bool, Error> {
|
||||||
|
if equals(file_path, "LOOT") {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_in_game_path(file_path) {
|
||||||
|
return Err(Error::InvalidPath(file_path.to_path_buf()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = state.data_path.join(file_path);
|
||||||
|
let exists = path.exists();
|
||||||
|
|
||||||
|
if !exists && has_plugin_file_extension(&path, state) {
|
||||||
|
Ok(add_extension(&path, "ghost").exists())
|
||||||
|
} else {
|
||||||
|
Ok(exists)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn eval(&self) -> Result<bool, Error> {
|
pub fn eval(&self, state: &State) -> Result<bool, Error> {
|
||||||
// TODO: Handle all variants.
|
// TODO: Handle all variants.
|
||||||
// TODO: Paths may not lead outside game directory.
|
// TODO: Paths may not lead outside game directory.
|
||||||
match *self {
|
match *self {
|
||||||
Function::FilePath(ref f) => Ok(f.exists()),
|
Function::FilePath(ref f) => evaluate_file_path(state, f),
|
||||||
_ => Ok(false),
|
_ => Ok(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,49 +79,103 @@ impl Function {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use function::Function;
|
use super::*;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::fs::{copy, create_dir};
|
||||||
|
|
||||||
|
use tempfile::tempdir;
|
||||||
|
|
||||||
|
use GameType;
|
||||||
|
|
||||||
|
fn state<T: Into<PathBuf>>(data_path: T) -> State {
|
||||||
|
let data_path = data_path.into();
|
||||||
|
if !data_path.exists() {
|
||||||
|
create_dir(&data_path).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
State {
|
||||||
|
game_type: GameType::tes4,
|
||||||
|
data_path: data_path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_file_path_eval_should_return_true_if_the_file_exists_relative_to_the_data_path() {
|
fn function_file_path_eval_should_return_true_if_the_file_exists_relative_to_the_data_path() {
|
||||||
let function = Function::FilePath(PathBuf::from("Cargo.toml"));
|
let function = Function::FilePath(PathBuf::from("Cargo.toml"));
|
||||||
|
let state = state(".");
|
||||||
|
|
||||||
assert!(function.eval().unwrap());
|
assert!(function.eval(&state).unwrap());
|
||||||
|
|
||||||
unimplemented!("not yet any way to actually specify the data path");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_file_path_eval_should_return_true_if_given_a_plugin_that_is_ghosted() {
|
fn function_file_path_eval_should_return_true_if_given_a_plugin_that_is_ghosted() {
|
||||||
let function = Function::FilePath(PathBuf::from("test.esp"));
|
let tmp_dir = tempdir().unwrap();
|
||||||
|
let data_path = tmp_dir.path().join("Data");
|
||||||
|
let state = state(data_path);
|
||||||
|
|
||||||
assert!(function.eval().unwrap());
|
copy(
|
||||||
|
Path::new("testing-plugins/Oblivion/Data/Blank.esp"),
|
||||||
|
&state.data_path.join("Blank.esp.ghost"),
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
unimplemented!("need to add tempdir and create a test.esp.ghost");
|
let function = Function::FilePath(PathBuf::from("Blank.esp"));
|
||||||
|
|
||||||
|
assert!(function.eval(&state).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn function_file_path_eval_should_be_true_if_given_LOOT() {
|
fn function_file_path_eval_should_be_true_if_given_LOOT() {
|
||||||
unimplemented!();
|
let function = Function::FilePath(PathBuf::from("LOOT"));
|
||||||
|
let state = state(".");
|
||||||
|
|
||||||
|
assert!(function.eval(&state).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_file_path_eval_should_not_check_for_ghosted_non_plugin_file() {
|
fn function_file_path_eval_should_not_check_for_ghosted_non_plugin_file() {
|
||||||
unimplemented!();
|
let tmp_dir = tempdir().unwrap();
|
||||||
|
let data_path = tmp_dir.path().join("Data");
|
||||||
|
let state = state(data_path);
|
||||||
|
|
||||||
|
copy(
|
||||||
|
Path::new("Cargo.toml"),
|
||||||
|
&state.data_path.join("Cargo.toml.ghost"),
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
let function = Function::FilePath(PathBuf::from("Cargo.toml"));
|
||||||
|
|
||||||
|
assert!(!function.eval(&state).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_file_path_eval_should_not_error_if_the_path_is_in_the_game_directory() {
|
||||||
|
let tmp_dir = tempdir().unwrap();
|
||||||
|
let data_path = tmp_dir.path().join("Data");
|
||||||
|
let state = state(data_path);
|
||||||
|
|
||||||
|
let function = Function::FilePath(PathBuf::from("../Cargo.toml"));
|
||||||
|
|
||||||
|
assert!(function.eval(&state).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_file_path_eval_should_error_if_the_path_is_outside_game_directory() {
|
fn function_file_path_eval_should_error_if_the_path_is_outside_game_directory() {
|
||||||
unimplemented!("to do");
|
let tmp_dir = tempdir().unwrap();
|
||||||
|
let data_path = tmp_dir.path().join("Data");
|
||||||
|
let state = state(data_path);
|
||||||
|
|
||||||
|
let function = Function::FilePath(PathBuf::from("../../Cargo.toml"));
|
||||||
|
|
||||||
|
assert!(function.eval(&state).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_file_path_eval_should_return_false_if_the_file_does_not_exist() {
|
fn function_file_path_eval_should_return_false_if_the_file_does_not_exist() {
|
||||||
let function = Function::FilePath(PathBuf::from("missing"));
|
let function = Function::FilePath(PathBuf::from("missing"));
|
||||||
|
let state = state(".");
|
||||||
|
|
||||||
assert!(!function.eval().unwrap());
|
assert!(!function.eval(&state).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ fn parse_version_args(input: &str) -> IResult<&str, (PathBuf, &str, ComparisonOp
|
|||||||
>> tag!("\"")
|
>> tag!("\"")
|
||||||
>> ws!(tag!(","))
|
>> ws!(tag!(","))
|
||||||
>> operator: call!(ComparisonOperator::parse)
|
>> operator: call!(ComparisonOperator::parse)
|
||||||
>> ((PathBuf::from(path), version, operator))
|
>> (PathBuf::from(path), version, operator)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ fn parse_checksum_args(input: &str) -> IResult<&str, (PathBuf, u32)> {
|
|||||||
>> tag!("\"")
|
>> tag!("\"")
|
||||||
>> ws!(tag!(","))
|
>> ws!(tag!(","))
|
||||||
>> crc: flat_map!(call!(hex_digit), parse_crc)
|
>> crc: flat_map!(call!(hex_digit), parse_crc)
|
||||||
>> ((PathBuf::from(path), crc))
|
>> (PathBuf::from(path), crc)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,9 +161,7 @@ mod tests {
|
|||||||
let result = Function::parse("many(\"Cargo.*\")").unwrap().1;
|
let result = Function::parse("many(\"Cargo.*\")").unwrap().1;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Function::Many(r) => {
|
Function::Many(r) => assert_eq!(Regex::new("Cargo.*").unwrap().as_str(), r.as_str()),
|
||||||
assert_eq!(Regex::new("Cargo.*").unwrap().as_str(), r.as_str())
|
|
||||||
},
|
|
||||||
_ => panic!("Expected a many function"),
|
_ => panic!("Expected a many function"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+83
-18
@@ -2,10 +2,15 @@
|
|||||||
extern crate nom;
|
extern crate nom;
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
|
|
||||||
use nom::{Err, IResult};
|
#[cfg(test)]
|
||||||
|
extern crate tempfile;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use nom::{Err, IResult};
|
||||||
|
|
||||||
mod function;
|
mod function;
|
||||||
use function::Function;
|
use function::Function;
|
||||||
|
|
||||||
@@ -15,6 +20,7 @@ pub enum Error {
|
|||||||
ParsingError,
|
ParsingError,
|
||||||
InvalidPath(PathBuf),
|
InvalidPath(PathBuf),
|
||||||
InvalidRegex(String),
|
InvalidRegex(String),
|
||||||
|
IoError(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> From<Err<I>> for Error {
|
impl<I> From<Err<I>> for Error {
|
||||||
@@ -26,14 +32,45 @@ impl<I> From<Err<I>> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
fn from(error: io::Error) -> Self {
|
||||||
|
Error::IoError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum GameType {
|
||||||
|
tes4,
|
||||||
|
tes5,
|
||||||
|
tes5se,
|
||||||
|
tes5vr,
|
||||||
|
fo3,
|
||||||
|
fonv,
|
||||||
|
fo4,
|
||||||
|
fo4vr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameType {
|
||||||
|
fn supports_light_plugins(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
GameType::tes5se | GameType::tes5vr | GameType::fo4 | GameType::fo4vr => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct State {
|
||||||
|
game_type: GameType,
|
||||||
|
data_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
// Compound conditions joined by 'or'
|
// Compound conditions joined by 'or'
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Expression(Vec<CompoundCondition>);
|
pub struct Expression(Vec<CompoundCondition>);
|
||||||
|
|
||||||
impl Expression {
|
impl Expression {
|
||||||
pub fn eval(&self) -> Result<bool, Error> {
|
pub fn eval(&self, state: &State) -> Result<bool, Error> {
|
||||||
for compound_condition in &self.0 {
|
for compound_condition in &self.0 {
|
||||||
if compound_condition.eval()? {
|
if compound_condition.eval(state)? {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,9 +92,9 @@ impl Expression {
|
|||||||
struct CompoundCondition(Vec<Condition>);
|
struct CompoundCondition(Vec<Condition>);
|
||||||
|
|
||||||
impl CompoundCondition {
|
impl CompoundCondition {
|
||||||
fn eval(&self) -> Result<bool, Error> {
|
fn eval(&self, state: &State) -> Result<bool, Error> {
|
||||||
for condition in &self.0 {
|
for condition in &self.0 {
|
||||||
if !condition.eval()? {
|
if !condition.eval(state)? {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,11 +118,11 @@ enum Condition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Condition {
|
impl Condition {
|
||||||
fn eval(&self) -> Result<bool, Error> {
|
fn eval(&self, state: &State) -> Result<bool, Error> {
|
||||||
match *self {
|
match *self {
|
||||||
Condition::Function(ref f) => f.eval(),
|
Condition::Function(ref f) => f.eval(state),
|
||||||
Condition::InvertedFunction(ref f) => f.eval().map(|r| !r),
|
Condition::InvertedFunction(ref f) => f.eval(state).map(|r| !r),
|
||||||
Condition::Expression(ref e) => e.eval(),
|
Condition::Expression(ref e) => e.eval(state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +149,20 @@ impl Condition {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use std::fs::create_dir;
|
||||||
|
|
||||||
|
fn state<T: Into<PathBuf>>(data_path: T) -> State {
|
||||||
|
let data_path = data_path.into();
|
||||||
|
if !data_path.exists() {
|
||||||
|
create_dir(&data_path).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
State {
|
||||||
|
game_type: GameType::tes4,
|
||||||
|
data_path: data_path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expression_parse_should_handle_a_single_compound_condition() {
|
fn expression_parse_should_handle_a_single_compound_condition() {
|
||||||
let result = Expression::parse("file(\"Cargo.toml\")").unwrap().1;
|
let result = Expression::parse("file(\"Cargo.toml\")").unwrap().1;
|
||||||
@@ -216,58 +267,70 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn condition_eval_should_return_function_eval_for_a_function_condition() {
|
fn condition_eval_should_return_function_eval_for_a_function_condition() {
|
||||||
|
let state = state(".");
|
||||||
|
|
||||||
let condition = Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml")));
|
let condition = Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml")));
|
||||||
|
|
||||||
assert!(condition.eval().unwrap());
|
assert!(condition.eval(&state).unwrap());
|
||||||
|
|
||||||
let condition = Condition::Function(Function::FilePath(PathBuf::from("missing")));
|
let condition = Condition::Function(Function::FilePath(PathBuf::from("missing")));
|
||||||
|
|
||||||
assert!(!condition.eval().unwrap());
|
assert!(!condition.eval(&state).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn condition_eval_should_return_expression_eval_for_an_expression_condition() {
|
fn condition_eval_should_return_expression_eval_for_an_expression_condition() {
|
||||||
|
let state = state(".");
|
||||||
|
|
||||||
let condition = Condition::Expression(Expression(vec![CompoundCondition(vec![
|
let condition = Condition::Expression(Expression(vec![CompoundCondition(vec![
|
||||||
Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml"))),
|
Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml"))),
|
||||||
])]));
|
])]));
|
||||||
|
|
||||||
assert!(condition.eval().unwrap());
|
assert!(condition.eval(&state).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn condition_eval_should_return_inverse_of_function_eval_for_a_not_function_condition() {
|
fn condition_eval_should_return_inverse_of_function_eval_for_a_not_function_condition() {
|
||||||
|
let state = state(".");
|
||||||
|
|
||||||
let condition =
|
let condition =
|
||||||
Condition::InvertedFunction(Function::FilePath(PathBuf::from("Cargo.toml")));
|
Condition::InvertedFunction(Function::FilePath(PathBuf::from("Cargo.toml")));
|
||||||
|
|
||||||
assert!(!condition.eval().unwrap());
|
assert!(!condition.eval(&state).unwrap());
|
||||||
|
|
||||||
let condition = Condition::InvertedFunction(Function::FilePath(PathBuf::from("missing")));
|
let condition = Condition::InvertedFunction(Function::FilePath(PathBuf::from("missing")));
|
||||||
|
|
||||||
assert!(condition.eval().unwrap());
|
assert!(condition.eval(&state).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compound_condition_eval_should_be_true_if_all_conditions_are_true() {
|
fn compound_condition_eval_should_be_true_if_all_conditions_are_true() {
|
||||||
|
let state = state(".");
|
||||||
|
|
||||||
let compound_condition = CompoundCondition(vec![
|
let compound_condition = CompoundCondition(vec![
|
||||||
Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml"))),
|
Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml"))),
|
||||||
Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml"))),
|
Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml"))),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert!(compound_condition.eval().unwrap());
|
assert!(compound_condition.eval(&state).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compound_condition_eval_should_be_false_if_any_condition_is_false() {
|
fn compound_condition_eval_should_be_false_if_any_condition_is_false() {
|
||||||
|
let state = state(".");
|
||||||
|
|
||||||
let compound_condition = CompoundCondition(vec![
|
let compound_condition = CompoundCondition(vec![
|
||||||
Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml"))),
|
Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml"))),
|
||||||
Condition::Function(Function::FilePath(PathBuf::from("missing"))),
|
Condition::Function(Function::FilePath(PathBuf::from("missing"))),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert!(!compound_condition.eval().unwrap());
|
assert!(!compound_condition.eval(&state).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expression_eval_should_be_true_if_any_compound_condition_is_true() {
|
fn expression_eval_should_be_true_if_any_compound_condition_is_true() {
|
||||||
|
let state = state(".");
|
||||||
|
|
||||||
let expression = Expression(vec![
|
let expression = Expression(vec![
|
||||||
CompoundCondition(vec![Condition::Function(Function::FilePath(
|
CompoundCondition(vec![Condition::Function(Function::FilePath(
|
||||||
PathBuf::from("Cargo.toml"),
|
PathBuf::from("Cargo.toml"),
|
||||||
@@ -276,11 +339,13 @@ mod tests {
|
|||||||
PathBuf::from("missing"),
|
PathBuf::from("missing"),
|
||||||
))]),
|
))]),
|
||||||
]);
|
]);
|
||||||
assert!(expression.eval().unwrap());
|
assert!(expression.eval(&state).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expression_eval_should_be_false_if_all_compound_conditions_are_false() {
|
fn expression_eval_should_be_false_if_all_compound_conditions_are_false() {
|
||||||
|
let state = state(".");
|
||||||
|
|
||||||
let expression = Expression(vec![
|
let expression = Expression(vec![
|
||||||
CompoundCondition(vec![Condition::Function(Function::FilePath(
|
CompoundCondition(vec![Condition::Function(Function::FilePath(
|
||||||
PathBuf::from("missing"),
|
PathBuf::from("missing"),
|
||||||
@@ -289,6 +354,6 @@ mod tests {
|
|||||||
PathBuf::from("missing"),
|
PathBuf::from("missing"),
|
||||||
))]),
|
))]),
|
||||||
]);
|
]);
|
||||||
assert!(!expression.eval().unwrap());
|
assert!(!expression.eval(&state).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user