rust_rule_engine/parser/
grl.rs1use crate::engine::rule::{Condition, ConditionGroup, Rule};
2use crate::errors::{Result, RuleEngineError};
3use crate::types::{ActionType, Operator, Value};
4use regex::Regex;
5use std::collections::HashMap;
6
7pub struct GRLParser;
10
11impl GRLParser {
12 pub fn parse_rule(grl_text: &str) -> Result<Rule> {
25 let mut parser = GRLParser;
26 parser.parse_single_rule(grl_text)
27 }
28
29 pub fn parse_rules(grl_text: &str) -> Result<Vec<Rule>> {
31 let mut parser = GRLParser;
32 parser.parse_multiple_rules(grl_text)
33 }
34
35 fn parse_single_rule(&mut self, grl_text: &str) -> Result<Rule> {
36 let cleaned = self.clean_text(grl_text);
37
38 let rule_regex =
40 Regex::new(r#"rule\s+(?:"([^"]+)"|(\w+))\s*(?:salience\s+(\d+))?\s*\{(.+)\}"#)
41 .map_err(|e| RuleEngineError::ParseError {
42 message: format!("Invalid rule regex: {}", e),
43 })?;
44
45 let captures =
46 rule_regex
47 .captures(&cleaned)
48 .ok_or_else(|| RuleEngineError::ParseError {
49 message: format!("Invalid GRL rule format. Input: {}", cleaned),
50 })?;
51
52 let rule_name = if let Some(quoted_name) = captures.get(1) {
54 quoted_name.as_str().to_string()
55 } else if let Some(unquoted_name) = captures.get(2) {
56 unquoted_name.as_str().to_string()
57 } else {
58 return Err(RuleEngineError::ParseError {
59 message: "Could not extract rule name".to_string(),
60 });
61 };
62 let salience = captures
64 .get(3)
65 .and_then(|m| m.as_str().parse::<i32>().ok())
66 .unwrap_or(0);
67
68 let rule_body = captures.get(4).unwrap().as_str();
69
70 let when_then_regex =
72 Regex::new(r"when\s+(.+?)\s+then\s+(.+)").map_err(|e| RuleEngineError::ParseError {
73 message: format!("Invalid when-then regex: {}", e),
74 })?;
75
76 let when_then_captures =
77 when_then_regex
78 .captures(rule_body)
79 .ok_or_else(|| RuleEngineError::ParseError {
80 message: "Missing when or then clause".to_string(),
81 })?;
82
83 let when_clause = when_then_captures.get(1).unwrap().as_str().trim();
84 let then_clause = when_then_captures.get(2).unwrap().as_str().trim();
85
86 let conditions = self.parse_when_clause(when_clause)?;
88
89 let actions = self.parse_then_clause(then_clause)?;
91
92 let mut rule = Rule::new(rule_name, conditions, actions);
94 rule = rule.with_priority(salience);
95
96 Ok(rule)
97 }
98
99 fn parse_multiple_rules(&mut self, grl_text: &str) -> Result<Vec<Rule>> {
100 let rule_regex =
102 Regex::new(r"rule\s+\w+[^}]*\}").map_err(|e| RuleEngineError::ParseError {
103 message: format!("Rule splitting regex error: {}", e),
104 })?;
105
106 let mut rules = Vec::new();
107
108 for rule_match in rule_regex.find_iter(grl_text) {
109 let rule_text = rule_match.as_str();
110 let rule = self.parse_single_rule(rule_text)?;
111 rules.push(rule);
112 }
113
114 Ok(rules)
115 }
116
117 fn clean_text(&self, text: &str) -> String {
118 text.lines()
119 .map(|line| line.trim())
120 .filter(|line| !line.is_empty() && !line.starts_with("//"))
121 .collect::<Vec<_>>()
122 .join(" ")
123 }
124
125 fn parse_when_clause(&self, when_clause: &str) -> Result<ConditionGroup> {
126 if when_clause.contains("&&") {
128 return self.parse_and_condition(when_clause);
129 }
130
131 if when_clause.contains("||") {
132 return self.parse_or_condition(when_clause);
133 }
134
135 if when_clause.starts_with("!") {
136 return self.parse_not_condition(when_clause);
137 }
138
139 self.parse_single_condition(when_clause)
141 }
142
143 fn parse_and_condition(&self, clause: &str) -> Result<ConditionGroup> {
144 let parts: Vec<&str> = clause.split("&&").collect();
145 if parts.len() < 2 {
146 return Err(RuleEngineError::ParseError {
147 message: "Invalid AND condition".to_string(),
148 });
149 }
150
151 let mut conditions = Vec::new();
152 for part in parts {
153 let condition = self.parse_when_clause(part.trim())?;
154 conditions.push(condition);
155 }
156
157 if conditions.is_empty() {
159 return Err(RuleEngineError::ParseError {
160 message: "No conditions found".to_string(),
161 });
162 }
163
164 let mut iter = conditions.into_iter();
165 let mut result = iter.next().unwrap();
166 for condition in iter {
167 result = ConditionGroup::and(result, condition);
168 }
169
170 Ok(result)
171 }
172
173 fn parse_or_condition(&self, clause: &str) -> Result<ConditionGroup> {
174 let parts: Vec<&str> = clause.split("||").collect();
175 if parts.len() < 2 {
176 return Err(RuleEngineError::ParseError {
177 message: "Invalid OR condition".to_string(),
178 });
179 }
180
181 let mut conditions = Vec::new();
182 for part in parts {
183 let condition = self.parse_when_clause(part.trim())?;
184 conditions.push(condition);
185 }
186
187 if conditions.is_empty() {
189 return Err(RuleEngineError::ParseError {
190 message: "No conditions found".to_string(),
191 });
192 }
193
194 let mut iter = conditions.into_iter();
195 let mut result = iter.next().unwrap();
196 for condition in iter {
197 result = ConditionGroup::or(result, condition);
198 }
199
200 Ok(result)
201 }
202
203 fn parse_not_condition(&self, clause: &str) -> Result<ConditionGroup> {
204 let inner_clause = clause.strip_prefix("!").unwrap().trim();
205 let inner_condition = self.parse_when_clause(inner_clause)?;
206 Ok(ConditionGroup::not(inner_condition))
207 }
208
209 fn parse_single_condition(&self, clause: &str) -> Result<ConditionGroup> {
210 let typed_object_regex =
212 Regex::new(r#"\$(\w+)\s*:\s*(\w+)\s*\(\s*(.+?)\s*\)"#).map_err(|e| {
213 RuleEngineError::ParseError {
214 message: format!("Typed object regex error: {}", e),
215 }
216 })?;
217
218 if let Some(captures) = typed_object_regex.captures(clause) {
219 let _object_name = captures.get(1).unwrap().as_str();
220 let _object_type = captures.get(2).unwrap().as_str();
221 let conditions_str = captures.get(3).unwrap().as_str();
222
223 return self.parse_conditions_within_object(conditions_str);
225 }
226
227 let condition_regex = Regex::new(
229 r#"(\w+(?:\.\w+)*)\s*(>=|<=|==|!=|>|<|contains|matches)\s*(.+)"#,
230 )
231 .map_err(|e| RuleEngineError::ParseError {
232 message: format!("Condition regex error: {}", e),
233 })?;
234
235 let captures =
236 condition_regex
237 .captures(clause)
238 .ok_or_else(|| RuleEngineError::ParseError {
239 message: format!("Invalid condition format: {}", clause),
240 })?;
241
242 let field = captures.get(1).unwrap().as_str().to_string();
243 let operator_str = captures.get(2).unwrap().as_str();
244 let value_str = captures.get(3).unwrap().as_str().trim();
245
246 let operator =
247 Operator::from_str(operator_str).ok_or_else(|| RuleEngineError::InvalidOperator {
248 operator: operator_str.to_string(),
249 })?;
250
251 let value = self.parse_value(value_str)?;
252
253 let condition = Condition::new(field, operator, value);
254 Ok(ConditionGroup::single(condition))
255 }
256
257 fn parse_conditions_within_object(&self, conditions_str: &str) -> Result<ConditionGroup> {
258 let parts: Vec<&str> = conditions_str.split("&&").collect();
260
261 let mut conditions = Vec::new();
262 for part in parts {
263 let trimmed = part.trim();
264 let condition = self.parse_simple_condition(trimmed)?;
265 conditions.push(condition);
266 }
267
268 if conditions.is_empty() {
270 return Err(RuleEngineError::ParseError {
271 message: "No conditions found".to_string(),
272 });
273 }
274
275 let mut iter = conditions.into_iter();
276 let mut result = iter.next().unwrap();
277 for condition in iter {
278 result = ConditionGroup::and(result, condition);
279 }
280
281 Ok(result)
282 }
283
284 fn parse_simple_condition(&self, clause: &str) -> Result<ConditionGroup> {
285 let condition_regex = Regex::new(r#"(\w+)\s*(>=|<=|==|!=|>|<)\s*(.+)"#).map_err(|e| {
287 RuleEngineError::ParseError {
288 message: format!("Simple condition regex error: {}", e),
289 }
290 })?;
291
292 let captures =
293 condition_regex
294 .captures(clause)
295 .ok_or_else(|| RuleEngineError::ParseError {
296 message: format!("Invalid simple condition format: {}", clause),
297 })?;
298
299 let field = captures.get(1).unwrap().as_str().to_string();
300 let operator_str = captures.get(2).unwrap().as_str();
301 let value_str = captures.get(3).unwrap().as_str().trim();
302
303 let operator =
304 Operator::from_str(operator_str).ok_or_else(|| RuleEngineError::InvalidOperator {
305 operator: operator_str.to_string(),
306 })?;
307
308 let value = self.parse_value(value_str)?;
309
310 let condition = Condition::new(field, operator, value);
311 Ok(ConditionGroup::single(condition))
312 }
313
314 fn parse_value(&self, value_str: &str) -> Result<Value> {
315 let trimmed = value_str.trim();
316
317 if (trimmed.starts_with('"') && trimmed.ends_with('"'))
319 || (trimmed.starts_with('\'') && trimmed.ends_with('\''))
320 {
321 let unquoted = &trimmed[1..trimmed.len() - 1];
322 return Ok(Value::String(unquoted.to_string()));
323 }
324
325 if trimmed.eq_ignore_ascii_case("true") {
327 return Ok(Value::Boolean(true));
328 }
329 if trimmed.eq_ignore_ascii_case("false") {
330 return Ok(Value::Boolean(false));
331 }
332
333 if trimmed.eq_ignore_ascii_case("null") {
335 return Ok(Value::Null);
336 }
337
338 if let Ok(int_val) = trimmed.parse::<i64>() {
340 return Ok(Value::Integer(int_val));
341 }
342
343 if let Ok(float_val) = trimmed.parse::<f64>() {
344 return Ok(Value::Number(float_val));
345 }
346
347 if trimmed.contains('.') {
349 return Ok(Value::String(trimmed.to_string()));
350 }
351
352 Ok(Value::String(trimmed.to_string()))
354 }
355
356 fn parse_then_clause(&self, then_clause: &str) -> Result<Vec<ActionType>> {
357 let statements: Vec<&str> = then_clause
358 .split(';')
359 .map(|s| s.trim())
360 .filter(|s| !s.is_empty())
361 .collect();
362
363 let mut actions = Vec::new();
364
365 for statement in statements {
366 let action = self.parse_action_statement(statement)?;
367 actions.push(action);
368 }
369
370 Ok(actions)
371 }
372
373 fn parse_action_statement(&self, statement: &str) -> Result<ActionType> {
374 let trimmed = statement.trim();
375
376 let method_regex = Regex::new(r#"\$(\w+)\.(\w+)\s*\(([^)]*)\)"#).map_err(|e| {
378 RuleEngineError::ParseError {
379 message: format!("Method regex error: {}", e),
380 }
381 })?;
382
383 if let Some(captures) = method_regex.captures(trimmed) {
384 let object = captures.get(1).unwrap().as_str().to_string();
385 let method = captures.get(2).unwrap().as_str().to_string();
386 let args_str = captures.get(3).unwrap().as_str();
387
388 let args = if args_str.trim().is_empty() {
389 Vec::new()
390 } else {
391 self.parse_method_args(args_str)?
392 };
393
394 return Ok(ActionType::MethodCall {
395 object,
396 method,
397 args,
398 });
399 }
400
401 if let Some(eq_pos) = trimmed.find('=') {
403 let field = trimmed[..eq_pos].trim().to_string();
404 let value_str = trimmed[eq_pos + 1..].trim();
405 let value = self.parse_value(value_str)?;
406
407 return Ok(ActionType::Set { field, value });
408 }
409
410 let func_regex =
412 Regex::new(r#"(\w+)\s*\(\s*(.+?)?\s*\)"#).map_err(|e| RuleEngineError::ParseError {
413 message: format!("Function regex error: {}", e),
414 })?;
415
416 if let Some(captures) = func_regex.captures(trimmed) {
417 let function_name = captures.get(1).unwrap().as_str();
418 let args_str = captures.get(2).map(|m| m.as_str()).unwrap_or("");
419
420 match function_name.to_lowercase().as_str() {
421 "update" => {
422 let object_name = if let Some(stripped) = args_str.strip_prefix('$') {
424 stripped.to_string()
425 } else {
426 args_str.to_string()
427 };
428 Ok(ActionType::Update {
429 object: object_name,
430 })
431 }
432 "log" => {
433 let message = if args_str.is_empty() {
434 "Log message".to_string()
435 } else {
436 let value = self.parse_value(args_str.trim())?;
437 value.to_string()
438 };
439 Ok(ActionType::Log { message })
440 }
441 _ => {
442 let args = if args_str.is_empty() {
443 Vec::new()
444 } else {
445 args_str
446 .split(',')
447 .map(|arg| self.parse_value(arg.trim()))
448 .collect::<Result<Vec<_>>>()?
449 };
450 Ok(ActionType::Call {
451 function: function_name.to_string(),
452 args,
453 })
454 }
455 }
456 } else {
457 Ok(ActionType::Custom {
459 action_type: "statement".to_string(),
460 params: {
461 let mut params = HashMap::new();
462 params.insert("statement".to_string(), Value::String(trimmed.to_string()));
463 params
464 },
465 })
466 }
467 }
468
469 fn parse_method_args(&self, args_str: &str) -> Result<Vec<Value>> {
470 if args_str.trim().is_empty() {
471 return Ok(Vec::new());
472 }
473
474 let mut args = Vec::new();
476 let parts: Vec<&str> = args_str.split(',').collect();
477
478 for part in parts {
479 let trimmed = part.trim();
480
481 if trimmed.contains('+')
483 || trimmed.contains('-')
484 || trimmed.contains('*')
485 || trimmed.contains('/')
486 {
487 args.push(Value::String(trimmed.to_string()));
489 } else {
490 args.push(self.parse_value(trimmed)?);
491 }
492 }
493
494 Ok(args)
495 }
496}
497
498#[cfg(test)]
499mod tests {
500 use crate::parser::grl_parser::GRLParser as NewGRLParser;
501
502 #[test]
503 fn test_parse_simple_rule() {
504 let grl = r#"
505 rule "CheckAge" salience 10 {
506 when
507 User.Age >= 18
508 then
509 log("User is adult");
510 }
511 "#;
512
513 let rules = NewGRLParser::parse_rules(grl).unwrap();
514 assert_eq!(rules.len(), 1);
515 let rule = &rules[0];
516 assert_eq!(rule.name, "CheckAge");
517 assert_eq!(rule.salience, 10);
518 assert_eq!(rule.actions.len(), 1);
519 }
520
521 #[test]
522 fn test_parse_complex_condition() {
523 let grl = r#"
524 rule "ComplexRule" {
525 when
526 User.Age >= 18 && User.Country == "US"
527 then
528 User.Qualified = true;
529 }
530 "#;
531
532 let rules = NewGRLParser::parse_rules(grl).unwrap();
533 assert_eq!(rules.len(), 1);
534 let rule = &rules[0];
535 assert_eq!(rule.name, "ComplexRule");
536 }
537}