Skip to content

Commit e830395

Browse files
committed
support multiple rules per category
Signed-off-by: Jean Mertz <[email protected]>
1 parent c640af8 commit e830395

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

aw-query/src/datatype.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use std::collections::HashMap;
22
use std::fmt;
3+
use std::str::FromStr as _;
34

45
use super::functions;
56
use super::QueryError;
67
use aw_models::Event;
7-
use aw_transform::classify::{KeyValueRule, RegexRule, Rule};
8+
use aw_transform::classify::{KeyValueRule, LogicalOperator, LogicalRule, RegexRule, Rule};
89

910
use serde::{Serialize, Serializer};
1011
use serde_json::value::Value;
@@ -300,6 +301,32 @@ impl TryFrom<&DataType> for Rule {
300301

301302
match rtype.as_str() {
302303
"none" => Ok(Self::None),
304+
"or" | "and" => {
305+
let Some(rules) = obj.get("rules") else {
306+
return Err(QueryError::InvalidFunctionParameters(format!(
307+
"{} rule is missing the 'rules' field",
308+
rtype
309+
)));
310+
};
311+
312+
let rules = match rules {
313+
DataType::List(rules) => rules
314+
.iter()
315+
.map(Rule::try_from)
316+
.collect::<Result<Vec<_>, _>>()?,
317+
_ => {
318+
return Err(QueryError::InvalidFunctionParameters(format!(
319+
"the rules field of the {} rule is not a list",
320+
rtype
321+
)))
322+
}
323+
};
324+
325+
let operator = LogicalOperator::from_str(rtype)
326+
.map_err(QueryError::InvalidFunctionParameters)?;
327+
328+
Ok(Rule::Logical(LogicalRule::new(rules, operator)))
329+
}
303330
"regex" => parse_regex_rule(obj),
304331
"keyvalue" => {
305332
let Some(rules) = obj.get("rules") else {

aw-transform/src/classify.rs

+42-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashMap;
1+
use std::{collections::HashMap, str::FromStr};
22

33
/// Transforms for classifying (tagging and categorizing) events.
44
///
@@ -8,6 +8,7 @@ use fancy_regex::Regex;
88

99
pub enum Rule {
1010
None,
11+
Logical(LogicalRule),
1112
Regex(RegexRule),
1213
KeyValue(KeyValueRule),
1314
}
@@ -16,6 +17,7 @@ impl RuleTrait for Rule {
1617
fn matches(&self, event: &Event) -> bool {
1718
match self {
1819
Rule::None => false,
20+
Rule::Logical(rule) => rule.matches(event),
1921
Rule::Regex(rule) => rule.matches(event),
2022
Rule::KeyValue(rule) => rule.matches(event),
2123
}
@@ -92,6 +94,45 @@ impl RuleTrait for KeyValueRule {
9294
}
9395
}
9496

97+
pub enum LogicalOperator {
98+
Or,
99+
And,
100+
}
101+
102+
impl FromStr for LogicalOperator {
103+
type Err = String;
104+
105+
fn from_str(s: &str) -> Result<Self, Self::Err> {
106+
match s {
107+
"or" => Ok(Self::Or),
108+
"and" => Ok(Self::And),
109+
_ => Err(format!("Invalid logical operator: {}", s)),
110+
}
111+
}
112+
}
113+
114+
pub struct LogicalRule {
115+
rules: Vec<Rule>,
116+
operator: LogicalOperator,
117+
}
118+
119+
impl LogicalRule {
120+
pub fn new(rules: Vec<Rule>, operator: LogicalOperator) -> Self {
121+
Self { rules, operator }
122+
}
123+
}
124+
125+
impl RuleTrait for LogicalRule {
126+
fn matches(&self, event: &Event) -> bool {
127+
use LogicalOperator::{And, Or};
128+
129+
match self.operator {
130+
Or => self.rules.iter().any(|rule| rule.matches(event)),
131+
And => self.rules.iter().all(|rule| rule.matches(event)),
132+
}
133+
}
134+
}
135+
95136
/// Categorizes a list of events
96137
///
97138
/// An event can only have one category, although the category may have a hierarchy,

0 commit comments

Comments
 (0)