mirror of
https://github.com/loot/loot-condition-interpreter.git
synced 2026-07-27 14:16:09 -07:00
Implement parsing for everything down to Function
This commit is contained in:
+168
-28
@@ -4,9 +4,9 @@ extern crate regex;
|
|||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use std::str;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use nom::{IError, IResult};
|
use nom::{IError, IResult};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::str;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Error {
|
enum Error {
|
||||||
@@ -25,6 +25,7 @@ impl From<IError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum ComparisonOperator {
|
enum ComparisonOperator {
|
||||||
Equal,
|
Equal,
|
||||||
NotEqual,
|
NotEqual,
|
||||||
@@ -34,6 +35,7 @@ enum ComparisonOperator {
|
|||||||
GreaterThanOrEqual,
|
GreaterThanOrEqual,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum Function {
|
enum Function {
|
||||||
FilePath(PathBuf),
|
FilePath(PathBuf),
|
||||||
FileRegex(Regex),
|
FileRegex(Regex),
|
||||||
@@ -60,13 +62,16 @@ impl Function {
|
|||||||
// TODO: Paths may not contain :*?"<>|
|
// TODO: Paths may not contain :*?"<>|
|
||||||
do_parse!(
|
do_parse!(
|
||||||
input,
|
input,
|
||||||
tag!("file(\"") >> path: is_not!("\"") >> tag!("\")")
|
tag!("file(\"")
|
||||||
|
>> path: is_not!("\"")
|
||||||
|
>> tag!("\")")
|
||||||
>> (Function::FilePath(PathBuf::from(path)))
|
>> (Function::FilePath(PathBuf::from(path)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compound conditions joined by 'or'
|
// Compound conditions joined by 'or'
|
||||||
|
#[derive(Debug)]
|
||||||
struct Expression(Vec<CompoundCondition>);
|
struct Expression(Vec<CompoundCondition>);
|
||||||
|
|
||||||
impl Expression {
|
impl Expression {
|
||||||
@@ -80,11 +85,17 @@ impl Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse(input: &str) -> IResult<&str, Expression> {
|
fn parse(input: &str) -> IResult<&str, Expression> {
|
||||||
IResult::Done(input, Expression(vec![]))
|
do_parse!(
|
||||||
|
input,
|
||||||
|
compound_conditions:
|
||||||
|
separated_list_complete!(ws!(tag!("or")), CompoundCondition::parse)
|
||||||
|
>> (Expression(compound_conditions))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conditions joined by 'and'
|
// Conditions joined by 'and'
|
||||||
|
#[derive(Debug)]
|
||||||
struct CompoundCondition(Vec<Condition>);
|
struct CompoundCondition(Vec<Condition>);
|
||||||
|
|
||||||
impl CompoundCondition {
|
impl CompoundCondition {
|
||||||
@@ -98,13 +109,18 @@ impl CompoundCondition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse(input: &str) -> IResult<&str, CompoundCondition> {
|
fn parse(input: &str) -> IResult<&str, CompoundCondition> {
|
||||||
IResult::Done(input, CompoundCondition(vec![]))
|
do_parse!(
|
||||||
|
input,
|
||||||
|
conditions: separated_list_complete!(ws!(tag!("and")), Condition::parse)
|
||||||
|
>> (CompoundCondition(conditions))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum Condition {
|
enum Condition {
|
||||||
Function(Function),
|
Function(Function),
|
||||||
NotFunction(Function),
|
InvertedFunction(Function),
|
||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,14 +128,27 @@ impl Condition {
|
|||||||
fn eval(&self) -> Result<bool, Error> {
|
fn eval(&self) -> Result<bool, Error> {
|
||||||
match *self {
|
match *self {
|
||||||
Condition::Function(ref f) => f.eval(),
|
Condition::Function(ref f) => f.eval(),
|
||||||
Condition::NotFunction(ref f) => f.eval().map(|r| !r),
|
Condition::InvertedFunction(ref f) => f.eval().map(|r| !r),
|
||||||
Condition::Expression(ref e) => e.eval(),
|
Condition::Expression(ref e) => e.eval(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(input: &str) -> IResult<&str, Condition> {
|
fn parse(input: &str) -> IResult<&str, Condition> {
|
||||||
let (input1, function) = try_parse!(input, Function::parse);
|
do_parse!(
|
||||||
IResult::Done(input1, Condition::Function(function))
|
input,
|
||||||
|
condition:
|
||||||
|
alt!(
|
||||||
|
call!(Function::parse) => {
|
||||||
|
|f| Condition::Function(f)
|
||||||
|
} |
|
||||||
|
preceded!(ws!(tag!("not")), call!(Function::parse)) => {
|
||||||
|
|f| Condition::InvertedFunction(f)
|
||||||
|
} |
|
||||||
|
delimited!(tag!("("), call!(Expression::parse), tag!(")")) => {
|
||||||
|
|e| Condition::Expression(e)
|
||||||
|
}
|
||||||
|
) >> (condition)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +156,118 @@ impl Condition {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expression_parse_should_handle_a_single_compound_condition() {
|
||||||
|
let result = Expression::parse("file(\"Cargo.toml\")")
|
||||||
|
.to_result()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match result.0.as_slice() {
|
||||||
|
[CompoundCondition(_)] => {}
|
||||||
|
_ => panic!("Expected an expression with one compound condition"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expression_parse_should_handle_multiple_compound_conditions() {
|
||||||
|
let result = Expression::parse("file(\"Cargo.toml\") or file(\"Cargo.toml\")")
|
||||||
|
.to_result()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match result.0.as_slice() {
|
||||||
|
[CompoundCondition(_), CompoundCondition(_)] => {}
|
||||||
|
v => panic!(
|
||||||
|
"Expected an expression with two compound conditions, got {:?}",
|
||||||
|
v
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compound_condition_parse_should_handle_a_single_condition() {
|
||||||
|
let result = CompoundCondition::parse("file(\"Cargo.toml\")")
|
||||||
|
.to_result()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match result.0.as_slice() {
|
||||||
|
[Condition::Function(Function::FilePath(f))] => {
|
||||||
|
assert_eq!(&PathBuf::from("Cargo.toml"), f)
|
||||||
|
}
|
||||||
|
v => panic!(
|
||||||
|
"Expected an expression with two compound conditions, got {:?}",
|
||||||
|
v
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compound_condition_parse_should_handle_multiple_conditions() {
|
||||||
|
let result = CompoundCondition::parse("file(\"Cargo.toml\") and file(\"README.md\")")
|
||||||
|
.to_result()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match result.0.as_slice() {
|
||||||
|
[Condition::Function(Function::FilePath(f1)), Condition::Function(Function::FilePath(f2))] =>
|
||||||
|
{
|
||||||
|
assert_eq!(&PathBuf::from("Cargo.toml"), f1);
|
||||||
|
assert_eq!(&PathBuf::from("README.md"), f2);
|
||||||
|
}
|
||||||
|
v => panic!(
|
||||||
|
"Expected an expression with two compound conditions, got {:?}",
|
||||||
|
v
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn condition_parse_should_handle_a_function() {
|
||||||
|
let result = Condition::parse("file(\"Cargo.toml\")")
|
||||||
|
.to_result()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Condition::Function(Function::FilePath(f)) => {
|
||||||
|
assert_eq!(PathBuf::from("Cargo.toml"), f)
|
||||||
|
}
|
||||||
|
v => panic!(
|
||||||
|
"Expected an expression with two compound conditions, got {:?}",
|
||||||
|
v
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn condition_parse_should_handle_a_inverted_function() {
|
||||||
|
let result = Condition::parse("not file(\"Cargo.toml\")")
|
||||||
|
.to_result()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Condition::InvertedFunction(Function::FilePath(f)) => {
|
||||||
|
assert_eq!(PathBuf::from("Cargo.toml"), f)
|
||||||
|
}
|
||||||
|
v => panic!(
|
||||||
|
"Expected an expression with two compound conditions, got {:?}",
|
||||||
|
v
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn condition_parse_should_handle_an_expression_in_parentheses() {
|
||||||
|
let result = Condition::parse("(not file(\"Cargo.toml\"))")
|
||||||
|
.to_result()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Condition::Expression(_) => {}
|
||||||
|
v => panic!(
|
||||||
|
"Expected an expression with two compound conditions, got {:?}",
|
||||||
|
v
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_parse_should_parse_a_file_function() {
|
fn function_parse_should_parse_a_file_function() {
|
||||||
let result = Function::parse("file(\"Cargo.toml\")").to_result().unwrap();
|
let result = Function::parse("file(\"Cargo.toml\")").to_result().unwrap();
|
||||||
@@ -180,22 +321,21 @@ mod tests {
|
|||||||
|
|
||||||
#[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 condition = Condition::Expression(Expression(vec![
|
let condition = Condition::Expression(Expression(vec![CompoundCondition(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().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 condition = Condition::NotFunction(Function::FilePath(PathBuf::from("Cargo.toml")));
|
let condition =
|
||||||
|
Condition::InvertedFunction(Function::FilePath(PathBuf::from("Cargo.toml")));
|
||||||
|
|
||||||
assert!(!condition.eval().unwrap());
|
assert!(!condition.eval().unwrap());
|
||||||
|
|
||||||
let condition = Condition::NotFunction(Function::FilePath(PathBuf::from("missing")));
|
let condition = Condition::InvertedFunction(Function::FilePath(PathBuf::from("missing")));
|
||||||
|
|
||||||
assert!(condition.eval().unwrap());
|
assert!(condition.eval().unwrap());
|
||||||
}
|
}
|
||||||
@@ -223,12 +363,12 @@ mod tests {
|
|||||||
#[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 expression = Expression(vec![
|
let expression = Expression(vec![
|
||||||
CompoundCondition(vec![
|
CompoundCondition(vec![Condition::Function(Function::FilePath(
|
||||||
Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml"))),
|
PathBuf::from("Cargo.toml"),
|
||||||
]),
|
))]),
|
||||||
CompoundCondition(vec![
|
CompoundCondition(vec![Condition::Function(Function::FilePath(
|
||||||
Condition::Function(Function::FilePath(PathBuf::from("missing"))),
|
PathBuf::from("missing"),
|
||||||
]),
|
))]),
|
||||||
]);
|
]);
|
||||||
assert!(expression.eval().unwrap());
|
assert!(expression.eval().unwrap());
|
||||||
}
|
}
|
||||||
@@ -236,12 +376,12 @@ mod tests {
|
|||||||
#[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 expression = Expression(vec![
|
let expression = Expression(vec![
|
||||||
CompoundCondition(vec![
|
CompoundCondition(vec![Condition::Function(Function::FilePath(
|
||||||
Condition::Function(Function::FilePath(PathBuf::from("missing"))),
|
PathBuf::from("missing"),
|
||||||
]),
|
))]),
|
||||||
CompoundCondition(vec![
|
CompoundCondition(vec![Condition::Function(Function::FilePath(
|
||||||
Condition::Function(Function::FilePath(PathBuf::from("missing"))),
|
PathBuf::from("missing"),
|
||||||
]),
|
))]),
|
||||||
]);
|
]);
|
||||||
assert!(!expression.eval().unwrap());
|
assert!(!expression.eval().unwrap());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user