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 = Regex::new(r#"rule\s+(?:"[^"]+"|[a-zA-Z_]\w*)[^}]*\}"#).map_err(|e| {
102 RuleEngineError::ParseError {
103 message: format!("Rule splitting regex error: {}", e),
104 }
105 })?;
106
107 let mut rules = Vec::new();
108
109 for rule_match in rule_regex.find_iter(grl_text) {
110 let rule_text = rule_match.as_str();
111 let rule = self.parse_single_rule(rule_text)?;
112 rules.push(rule);
113 }
114
115 Ok(rules)
116 }
117
118 fn clean_text(&self, text: &str) -> String {
119 text.lines()
120 .map(|line| line.trim())
121 .filter(|line| !line.is_empty() && !line.starts_with("//"))
122 .collect::<Vec<_>>()
123 .join(" ")
124 }
125
126 fn parse_when_clause(&self, when_clause: &str) -> Result<ConditionGroup> {
127 if when_clause.contains("&&") {
129 return self.parse_and_condition(when_clause);
130 }
131
132 if when_clause.contains("||") {
133 return self.parse_or_condition(when_clause);
134 }
135
136 if when_clause.starts_with("!") {
137 return self.parse_not_condition(when_clause);
138 }
139
140 self.parse_single_condition(when_clause)
142 }
143
144 fn parse_and_condition(&self, clause: &str) -> Result<ConditionGroup> {
145 let parts: Vec<&str> = clause.split("&&").collect();
146 if parts.len() < 2 {
147 return Err(RuleEngineError::ParseError {
148 message: "Invalid AND condition".to_string(),
149 });
150 }
151
152 let mut conditions = Vec::new();
153 for part in parts {
154 let condition = self.parse_when_clause(part.trim())?;
155 conditions.push(condition);
156 }
157
158 if conditions.is_empty() {
160 return Err(RuleEngineError::ParseError {
161 message: "No conditions found".to_string(),
162 });
163 }
164
165 let mut iter = conditions.into_iter();
166 let mut result = iter.next().unwrap();
167 for condition in iter {
168 result = ConditionGroup::and(result, condition);
169 }
170
171 Ok(result)
172 }
173
174 fn parse_or_condition(&self, clause: &str) -> Result<ConditionGroup> {
175 let parts: Vec<&str> = clause.split("||").collect();
176 if parts.len() < 2 {
177 return Err(RuleEngineError::ParseError {
178 message: "Invalid OR condition".to_string(),
179 });
180 }
181
182 let mut conditions = Vec::new();
183 for part in parts {
184 let condition = self.parse_when_clause(part.trim())?;
185 conditions.push(condition);
186 }
187
188 if conditions.is_empty() {
190 return Err(RuleEngineError::ParseError {
191 message: "No conditions found".to_string(),
192 });
193 }
194
195 let mut iter = conditions.into_iter();
196 let mut result = iter.next().unwrap();
197 for condition in iter {
198 result = ConditionGroup::or(result, condition);
199 }
200
201 Ok(result)
202 }
203
204 fn parse_not_condition(&self, clause: &str) -> Result<ConditionGroup> {
205 let inner_clause = clause.strip_prefix("!").unwrap().trim();
206 let inner_condition = self.parse_when_clause(inner_clause)?;
207 Ok(ConditionGroup::not(inner_condition))
208 }
209
210 fn parse_single_condition(&self, clause: &str) -> Result<ConditionGroup> {
211 let typed_object_regex =
213 Regex::new(r#"\$(\w+)\s*:\s*(\w+)\s*\(\s*(.+?)\s*\)"#).map_err(|e| {
214 RuleEngineError::ParseError {
215 message: format!("Typed object regex error: {}", e),
216 }
217 })?;
218
219 if let Some(captures) = typed_object_regex.captures(clause) {
220 let _object_name = captures.get(1).unwrap().as_str();
221 let _object_type = captures.get(2).unwrap().as_str();
222 let conditions_str = captures.get(3).unwrap().as_str();
223
224 return self.parse_conditions_within_object(conditions_str);
226 }
227
228 let condition_regex = Regex::new(
230 r#"(\w+(?:\.\w+)*)\s*(>=|<=|==|!=|>|<|contains|matches)\s*(.+)"#,
231 )
232 .map_err(|e| RuleEngineError::ParseError {
233 message: format!("Condition regex error: {}", e),
234 })?;
235
236 let captures =
237 condition_regex
238 .captures(clause)
239 .ok_or_else(|| RuleEngineError::ParseError {
240 message: format!("Invalid condition format: {}", clause),
241 })?;
242
243 let field = captures.get(1).unwrap().as_str().to_string();
244 let operator_str = captures.get(2).unwrap().as_str();
245 let value_str = captures.get(3).unwrap().as_str().trim();
246
247 let operator =
248 Operator::from_str(operator_str).ok_or_else(|| RuleEngineError::InvalidOperator {
249 operator: operator_str.to_string(),
250 })?;
251
252 let value = self.parse_value(value_str)?;
253
254 let condition = Condition::new(field, operator, value);
255 Ok(ConditionGroup::single(condition))
256 }
257
258 fn parse_conditions_within_object(&self, conditions_str: &str) -> Result<ConditionGroup> {
259 let parts: Vec<&str> = conditions_str.split("&&").collect();
261
262 let mut conditions = Vec::new();
263 for part in parts {
264 let trimmed = part.trim();
265 let condition = self.parse_simple_condition(trimmed)?;
266 conditions.push(condition);
267 }
268
269 if conditions.is_empty() {
271 return Err(RuleEngineError::ParseError {
272 message: "No conditions found".to_string(),
273 });
274 }
275
276 let mut iter = conditions.into_iter();
277 let mut result = iter.next().unwrap();
278 for condition in iter {
279 result = ConditionGroup::and(result, condition);
280 }
281
282 Ok(result)
283 }
284
285 fn parse_simple_condition(&self, clause: &str) -> Result<ConditionGroup> {
286 let condition_regex = Regex::new(r#"(\w+)\s*(>=|<=|==|!=|>|<)\s*(.+)"#).map_err(|e| {
288 RuleEngineError::ParseError {
289 message: format!("Simple condition regex error: {}", e),
290 }
291 })?;
292
293 let captures =
294 condition_regex
295 .captures(clause)
296 .ok_or_else(|| RuleEngineError::ParseError {
297 message: format!("Invalid simple condition format: {}", clause),
298 })?;
299
300 let field = captures.get(1).unwrap().as_str().to_string();
301 let operator_str = captures.get(2).unwrap().as_str();
302 let value_str = captures.get(3).unwrap().as_str().trim();
303
304 let operator =
305 Operator::from_str(operator_str).ok_or_else(|| RuleEngineError::InvalidOperator {
306 operator: operator_str.to_string(),
307 })?;
308
309 let value = self.parse_value(value_str)?;
310
311 let condition = Condition::new(field, operator, value);
312 Ok(ConditionGroup::single(condition))
313 }
314
315 fn parse_value(&self, value_str: &str) -> Result<Value> {
316 let trimmed = value_str.trim();
317
318 if (trimmed.starts_with('"') && trimmed.ends_with('"'))
320 || (trimmed.starts_with('\'') && trimmed.ends_with('\''))
321 {
322 let unquoted = &trimmed[1..trimmed.len() - 1];
323 return Ok(Value::String(unquoted.to_string()));
324 }
325
326 if trimmed.eq_ignore_ascii_case("true") {
328 return Ok(Value::Boolean(true));
329 }
330 if trimmed.eq_ignore_ascii_case("false") {
331 return Ok(Value::Boolean(false));
332 }
333
334 if trimmed.eq_ignore_ascii_case("null") {
336 return Ok(Value::Null);
337 }
338
339 if let Ok(int_val) = trimmed.parse::<i64>() {
341 return Ok(Value::Integer(int_val));
342 }
343
344 if let Ok(float_val) = trimmed.parse::<f64>() {
345 return Ok(Value::Number(float_val));
346 }
347
348 if trimmed.contains('.') {
350 return Ok(Value::String(trimmed.to_string()));
351 }
352
353 Ok(Value::String(trimmed.to_string()))
355 }
356
357 fn parse_then_clause(&self, then_clause: &str) -> Result<Vec<ActionType>> {
358 let statements: Vec<&str> = then_clause
359 .split(';')
360 .map(|s| s.trim())
361 .filter(|s| !s.is_empty())
362 .collect();
363
364 let mut actions = Vec::new();
365
366 for statement in statements {
367 let action = self.parse_action_statement(statement)?;
368 actions.push(action);
369 }
370
371 Ok(actions)
372 }
373
374 fn parse_action_statement(&self, statement: &str) -> Result<ActionType> {
375 let trimmed = statement.trim();
376
377 let method_regex = Regex::new(r#"\$(\w+)\.(\w+)\s*\(([^)]*)\)"#).map_err(|e| {
379 RuleEngineError::ParseError {
380 message: format!("Method regex error: {}", e),
381 }
382 })?;
383
384 if let Some(captures) = method_regex.captures(trimmed) {
385 let object = captures.get(1).unwrap().as_str().to_string();
386 let method = captures.get(2).unwrap().as_str().to_string();
387 let args_str = captures.get(3).unwrap().as_str();
388
389 let args = if args_str.trim().is_empty() {
390 Vec::new()
391 } else {
392 self.parse_method_args(args_str)?
393 };
394
395 return Ok(ActionType::MethodCall {
396 object,
397 method,
398 args,
399 });
400 }
401
402 if let Some(eq_pos) = trimmed.find('=') {
404 let field = trimmed[..eq_pos].trim().to_string();
405 let value_str = trimmed[eq_pos + 1..].trim();
406 let value = self.parse_value(value_str)?;
407
408 return Ok(ActionType::Set { field, value });
409 }
410
411 let func_regex =
413 Regex::new(r#"(\w+)\s*\(\s*(.+?)?\s*\)"#).map_err(|e| RuleEngineError::ParseError {
414 message: format!("Function regex error: {}", e),
415 })?;
416
417 if let Some(captures) = func_regex.captures(trimmed) {
418 let function_name = captures.get(1).unwrap().as_str();
419 let args_str = captures.get(2).map(|m| m.as_str()).unwrap_or("");
420
421 match function_name.to_lowercase().as_str() {
422 "update" => {
423 let object_name = if let Some(stripped) = args_str.strip_prefix('$') {
425 stripped.to_string()
426 } else {
427 args_str.to_string()
428 };
429 Ok(ActionType::Update {
430 object: object_name,
431 })
432 }
433 "log" => {
434 let message = if args_str.is_empty() {
435 "Log message".to_string()
436 } else {
437 let value = self.parse_value(args_str.trim())?;
438 value.to_string()
439 };
440 Ok(ActionType::Log { message })
441 }
442 _ => {
443 let args = if args_str.is_empty() {
444 Vec::new()
445 } else {
446 args_str
447 .split(',')
448 .map(|arg| self.parse_value(arg.trim()))
449 .collect::<Result<Vec<_>>>()?
450 };
451 Ok(ActionType::Call {
452 function: function_name.to_string(),
453 args,
454 })
455 }
456 }
457 } else {
458 Ok(ActionType::Custom {
460 action_type: "statement".to_string(),
461 params: {
462 let mut params = HashMap::new();
463 params.insert("statement".to_string(), Value::String(trimmed.to_string()));
464 params
465 },
466 })
467 }
468 }
469
470 fn parse_method_args(&self, args_str: &str) -> Result<Vec<Value>> {
471 if args_str.trim().is_empty() {
472 return Ok(Vec::new());
473 }
474
475 let mut args = Vec::new();
477 let parts: Vec<&str> = args_str.split(',').collect();
478
479 for part in parts {
480 let trimmed = part.trim();
481
482 if trimmed.contains('+')
484 || trimmed.contains('-')
485 || trimmed.contains('*')
486 || trimmed.contains('/')
487 {
488 args.push(Value::String(trimmed.to_string()));
490 } else {
491 args.push(self.parse_value(trimmed)?);
492 }
493 }
494
495 Ok(args)
496 }
497}
498
499#[cfg(test)]
500mod tests {
501 use crate::parser::grl_parser::GRLParser as NewGRLParser;
502
503 #[test]
504 fn test_parse_simple_rule() {
505 let grl = r#"
506 rule "CheckAge" salience 10 {
507 when
508 User.Age >= 18
509 then
510 log("User is adult");
511 }
512 "#;
513
514 let rules = NewGRLParser::parse_rules(grl).unwrap();
515 assert_eq!(rules.len(), 1);
516 let rule = &rules[0];
517 assert_eq!(rule.name, "CheckAge");
518 assert_eq!(rule.salience, 10);
519 assert_eq!(rule.actions.len(), 1);
520 }
521
522 #[test]
523 fn test_parse_complex_condition() {
524 let grl = r#"
525 rule "ComplexRule" {
526 when
527 User.Age >= 18 && User.Country == "US"
528 then
529 User.Qualified = true;
530 }
531 "#;
532
533 let rules = NewGRLParser::parse_rules(grl).unwrap();
534 assert_eq!(rules.len(), 1);
535 let rule = &rules[0];
536 assert_eq!(rule.name, "ComplexRule");
537 }
538}