Skip to content

Commit

Permalink
feat(rule-engine): add option field support in dsl
Browse files Browse the repository at this point in the history
  • Loading branch information
banditopazzo committed Feb 7, 2024
1 parent 1ab991d commit 44ee847
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 22 deletions.
57 changes: 41 additions & 16 deletions crates/modules/rules-engine/src/dsl.lalrpop
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use validatron::{Operator, RelationalOperator, StringOperator, MultiOperator, RValue, Field, Identifier, Condition, AdtField, SimpleField, MethodCall};
use lalrpop_util::ParseError;

use super::{DslError, IndentValue};
use super::{DslError, OptCheck};

grammar(variant: &str);

Expand All @@ -12,11 +12,11 @@ pub Condition: Condition = {
}

SignedCondition: Condition = {
"NOT" <b:BinaryCondition> => Condition::Not{inner: Box::new(b)},
BinaryCondition
"NOT" <b:SimpleCondition> => Condition::Not{inner: Box::new(b)},
SimpleCondition
}

BinaryCondition: Condition = {
SimpleCondition: Condition = {
<f: FieldPath> <op: Operator> <value: Value> => Condition::Binary {
l: f,
op,
Expand Down Expand Up @@ -90,7 +90,7 @@ Operator: Operator = {
}

FieldPath: Vec<Identifier> = {
<Dot<Ident>> =>? {
<IdentSeq> =>? {
let mut payload_subpath = false;

let mut v = Vec::new();
Expand All @@ -100,7 +100,7 @@ FieldPath: Vec<Identifier> = {

while let Some((index, value)) = dotted_idents.next() {
match value {
IndentValue::MethodCall(mc) => {
Identifier::MethodCall(mc) => {
if index == 0 {
return Err(ParseError::User {
error: DslError::FunctionCall
Expand All @@ -115,7 +115,7 @@ FieldPath: Vec<Identifier> = {
})
}
}
IndentValue::SimpleField(SimpleField(value)) => {
Identifier::Field(Field::Simple(SimpleField(value))) => {
if index == 0 && value == "payload" {
payload_subpath = true;
}
Expand All @@ -128,25 +128,50 @@ FieldPath: Vec<Identifier> = {

v.push(identifier);
}
Identifier::Field(Field::Adt(field)) => {
if index == 0 {
return Err(ParseError::User {
error: DslError::AdtFirstField
})
}

v.push(Identifier::Field(Field::Adt(field)));
}
}
}

Ok(v)
}
}

Dot<T>: Vec<T> = {
<v:T> <mut e:("." <T>)*> => if e.is_empty() {
vec![v]
} else {
e.insert(0, v);
e
IdentSeq: Vec<Identifier> = {
<v:(<Ident> <OptCheck?> ".")*> <e:Ident> => {
let mut res = Vec::new();

for (ident, opt_check) in v.into_iter() {
res.push(ident);

if opt_check.is_some() {
res.push(Identifier::Field(Field::Adt(AdtField {
variant_name: "Some".to_string(),
field_name: "0".to_string()
})));
}
}

res.push(e);

res
}
}

Ident: IndentValue = {
<s: r"[a-zA-Z]+\w*"> => IndentValue::SimpleField(SimpleField(<>.to_string())),
<s: r"[a-zA-Z]+\w*">"()" => IndentValue::MethodCall(MethodCall{ name: <>.to_string() })
OptCheck: OptCheck = {
"?" => OptCheck
}

Ident: Identifier = {
r"[a-zA-Z]+\w*" => Identifier::Field(Field::Simple(SimpleField(<>.to_string()))),
<r"[a-zA-Z]+\w*">"()" => Identifier::MethodCall(MethodCall{ name: <>.to_string() })
}

extern {
Expand Down
57 changes: 51 additions & 6 deletions crates/modules/rules-engine/src/dsl.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use lalrpop_util::lalrpop_mod;
use thiserror::Error;
use validatron::{MethodCall, SimpleField};

#[derive(Error, Debug)]
pub enum DslError {
Expand All @@ -10,13 +9,11 @@ pub enum DslError {
MethodCallNotFinal,
#[error("Function calls are not supported, only methods are allowed")]
FunctionCall,
#[error("Adt fields are not allowed as first field")]
AdtFirstField
}

#[derive(Debug, Clone, Eq, PartialEq)]
enum IndentValue {
SimpleField(SimpleField),
MethodCall(MethodCall),
}
struct OptCheck;

lalrpop_mod!(#[allow(clippy::all)] pub dsl); // syntesized by LALRPOP

Expand Down Expand Up @@ -435,4 +432,52 @@ mod tests {
]);
assert_eq!(parsed, expected);
}

#[test]
fn option_field() {
let parsed = dsl::ConditionParser::new()
.parse("FileExec", r#"header.container?.name == "ubuntu""#)
.unwrap();
let expected = Condition::Binary {
l: vec![
Identifier::Field(Field::Simple(SimpleField("header".to_string()))),
Identifier::Field(Field::Simple(SimpleField("container".to_string()))),
Identifier::Field(Field::Adt(AdtField {
variant_name: "Some".to_string(),
field_name: "0".to_string(),
})),
Identifier::Field(Field::Simple(SimpleField("name".to_string()))),
],
op: Operator::Relational(RelationalOperator::Equals),
r: RValue::Value("ubuntu".to_string()),
};
assert_eq!(parsed, expected);
}

#[test]
fn option_nested_field() {
let parsed = dsl::ConditionParser::new()
.parse("FileExec", r#"header.prop1?.prop2.prop3?.prop4 == "good""#)
.unwrap();
let expected = Condition::Binary {
l: vec![
Identifier::Field(Field::Simple(SimpleField("header".to_string()))),
Identifier::Field(Field::Simple(SimpleField("prop1".to_string()))),
Identifier::Field(Field::Adt(AdtField {
variant_name: "Some".to_string(),
field_name: "0".to_string(),
})),
Identifier::Field(Field::Simple(SimpleField("prop2".to_string()))),
Identifier::Field(Field::Simple(SimpleField("prop3".to_string()))),
Identifier::Field(Field::Adt(AdtField {
variant_name: "Some".to_string(),
field_name: "0".to_string(),
})),
Identifier::Field(Field::Simple(SimpleField("prop4".to_string()))),
],
op: Operator::Relational(RelationalOperator::Equals),
r: RValue::Value("good".to_string()),
};
assert_eq!(parsed, expected);
}
}

0 comments on commit 44ee847

Please sign in to comment.