mirror of
https://github.com/loot/loot-condition-interpreter.git
synced 2026-07-27 14:16:09 -07:00
Refactoring, add active(file) function parsing
This commit is contained in:
+115
@@ -0,0 +1,115 @@
|
|||||||
|
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
use nom::{IError, IResult};
|
||||||
|
|
||||||
|
use super::Error;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ComparisonOperator {
|
||||||
|
Equal,
|
||||||
|
NotEqual,
|
||||||
|
LessThan,
|
||||||
|
GreaterThan,
|
||||||
|
LessThanOrEqual,
|
||||||
|
GreaterThanOrEqual,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Function {
|
||||||
|
FilePath(PathBuf),
|
||||||
|
FileRegex(Regex),
|
||||||
|
ActivePath(PathBuf),
|
||||||
|
ActiveRegex(Regex),
|
||||||
|
Many(Regex),
|
||||||
|
ManyActive(Regex),
|
||||||
|
Checksum(PathBuf, u32),
|
||||||
|
Version(PathBuf, String, ComparisonOperator),
|
||||||
|
}
|
||||||
|
|
||||||
|
const INVALID_PATH_CHARS: &str = "\":*?<>|\\"; // \ is treated as invalid to distinguish regex strings.
|
||||||
|
const INVALID_REGEX_PATH_CHARS: &str = "\"<>";
|
||||||
|
|
||||||
|
impl Function {
|
||||||
|
pub fn eval(&self) -> Result<bool, Error> {
|
||||||
|
// TODO: Handle all variants.
|
||||||
|
// TODO: Paths may not lead outside game directory.
|
||||||
|
match *self {
|
||||||
|
Function::FilePath(ref f) => Ok(f.exists()),
|
||||||
|
_ => Ok(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(input: &str) -> IResult<&str, Function> {
|
||||||
|
// TODO: Handle all variants.
|
||||||
|
// TODO: Paths may not contain :*?"<>|
|
||||||
|
do_parse!(
|
||||||
|
input,
|
||||||
|
function: alt!(
|
||||||
|
delimited!(tag!("file(\""), is_not!(INVALID_PATH_CHARS), tag!("\")")) => {
|
||||||
|
|path| Function::FilePath(PathBuf::from(path))
|
||||||
|
} |
|
||||||
|
delimited!(tag!("active(\""), is_not!(INVALID_PATH_CHARS), tag!("\")")) => {
|
||||||
|
|path| Function::ActivePath(PathBuf::from(path))
|
||||||
|
}
|
||||||
|
) >> (function)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_parse_should_parse_a_file_path_function() {
|
||||||
|
let result = Function::parse("file(\"Cargo.toml\")").to_result().unwrap();
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Function::FilePath(f) => assert_eq!(PathBuf::from("Cargo.toml"), f),
|
||||||
|
_ => panic!("Expected a file path function"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_parse_should_parse_an_active_function() {
|
||||||
|
let result = Function::parse("active(\"Cargo.toml\")").to_result().unwrap();
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Function::ActivePath(f) => assert_eq!(PathBuf::from("Cargo.toml"), f),
|
||||||
|
_ => panic!("Expected an active path function"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
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"));
|
||||||
|
|
||||||
|
assert!(function.eval().unwrap());
|
||||||
|
|
||||||
|
unimplemented!("not yet any way to actually specify the data path");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_file_path_eval_should_return_true_if_given_a_plugin_that_is_ghosted() {
|
||||||
|
let function = Function::FilePath(PathBuf::from("test.esp"));
|
||||||
|
|
||||||
|
assert!(function.eval().unwrap());
|
||||||
|
|
||||||
|
unimplemented!("need to add tempdir and create a test.esp.ghost");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_file_path_eval_should_error_if_the_path_is_outside_game_directory() {
|
||||||
|
unimplemented!("to do");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_file_path_eval_should_return_false_if_the_file_does_not_exist() {
|
||||||
|
let function = Function::FilePath(PathBuf::from("missing"));
|
||||||
|
|
||||||
|
assert!(!function.eval().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-92
@@ -2,14 +2,15 @@
|
|||||||
extern crate nom;
|
extern crate nom;
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
|
|
||||||
use regex::Regex;
|
|
||||||
|
|
||||||
use nom::{IError, IResult};
|
use nom::{IError, IResult};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
mod function;
|
||||||
|
use function::Function;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Error {
|
pub enum Error {
|
||||||
ParsingIncomplete,
|
ParsingIncomplete,
|
||||||
ParsingError,
|
ParsingError,
|
||||||
InvalidPath(PathBuf),
|
InvalidPath(PathBuf),
|
||||||
@@ -25,57 +26,12 @@ impl From<IError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum ComparisonOperator {
|
|
||||||
Equal,
|
|
||||||
NotEqual,
|
|
||||||
LessThan,
|
|
||||||
GreaterThan,
|
|
||||||
LessThanOrEqual,
|
|
||||||
GreaterThanOrEqual,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum Function {
|
|
||||||
FilePath(PathBuf),
|
|
||||||
FileRegex(Regex),
|
|
||||||
ActivePath(PathBuf),
|
|
||||||
ActiveRegex(Regex),
|
|
||||||
Many(Regex),
|
|
||||||
ManyActive(Regex),
|
|
||||||
Checksum(PathBuf, u32),
|
|
||||||
Version(PathBuf, String, ComparisonOperator),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Function {
|
|
||||||
fn eval(&self) -> Result<bool, Error> {
|
|
||||||
// TODO: Handle all variants.
|
|
||||||
// TODO: Paths may not lead outside game directory.
|
|
||||||
match *self {
|
|
||||||
Function::FilePath(ref f) => Ok(f.exists()),
|
|
||||||
_ => Ok(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse(input: &str) -> IResult<&str, Function> {
|
|
||||||
// TODO: Handle all variants.
|
|
||||||
// TODO: Paths may not contain :*?"<>|
|
|
||||||
do_parse!(
|
|
||||||
input,
|
|
||||||
tag!("file(\"")
|
|
||||||
>> path: is_not!("\"")
|
|
||||||
>> tag!("\")")
|
|
||||||
>> (Function::FilePath(PathBuf::from(path)))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compound conditions joined by 'or'
|
// Compound conditions joined by 'or'
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Expression(Vec<CompoundCondition>);
|
pub struct Expression(Vec<CompoundCondition>);
|
||||||
|
|
||||||
impl Expression {
|
impl Expression {
|
||||||
fn eval(&self) -> Result<bool, Error> {
|
pub fn eval(&self) -> Result<bool, Error> {
|
||||||
for compound_condition in &self.0 {
|
for compound_condition in &self.0 {
|
||||||
if compound_condition.eval()? {
|
if compound_condition.eval()? {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
@@ -84,7 +40,7 @@ impl Expression {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(input: &str) -> IResult<&str, Expression> {
|
pub fn parse(input: &str) -> IResult<&str, Expression> {
|
||||||
do_parse!(
|
do_parse!(
|
||||||
input,
|
input,
|
||||||
compound_conditions:
|
compound_conditions:
|
||||||
@@ -268,46 +224,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn function_parse_should_parse_a_file_function() {
|
|
||||||
let result = Function::parse("file(\"Cargo.toml\")").to_result().unwrap();
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Function::FilePath(f) => assert_eq!(PathBuf::from("Cargo.toml"), f),
|
|
||||||
_ => panic!("Expected a file function"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
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"));
|
|
||||||
|
|
||||||
assert!(function.eval().unwrap());
|
|
||||||
|
|
||||||
unimplemented!("not yet any way to actually specify the data path");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn function_file_path_eval_should_return_true_if_given_a_plugin_that_is_ghosted() {
|
|
||||||
let function = Function::FilePath(PathBuf::from("test.esp"));
|
|
||||||
|
|
||||||
assert!(function.eval().unwrap());
|
|
||||||
|
|
||||||
unimplemented!("need to add tempdir and create a test.esp.ghost");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn function_file_path_eval_should_error_if_the_path_is_outside_game_directory() {
|
|
||||||
unimplemented!("to do");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn function_file_path_eval_should_return_false_if_the_file_does_not_exist() {
|
|
||||||
let function = Function::FilePath(PathBuf::from("missing"));
|
|
||||||
|
|
||||||
assert!(!function.eval().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 condition = Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml")));
|
let condition = Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml")));
|
||||||
|
|||||||
Reference in New Issue
Block a user