1use 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+(?:"([^"]+)"|([a-zA-Z_]\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 =
103 Regex::new(r#"(?s)rule\s+(?:"[^"]+"|[a-zA-Z_]\w*).*?\}"#).map_err(|e| {
104 RuleEngineError::ParseError {
105 message: format!("Rule splitting regex error: {}", e),
106 }
107 })?;
108
109 let mut rules = Vec::new();
110
111 for rule_match in rule_regex.find_iter(grl_text) {
112 let rule_text = rule_match.as_str();
113 let rule = self.parse_single_rule(rule_text)?;
114 rules.push(rule);
115 }
116
117 Ok(rules)
118 }
119
120 fn clean_text(&self, text: &str) -> String {
121 text.lines()
122 .map(|line| line.trim())
123 .filter(|line| !line.is_empty() && !line.starts_with("//"))
124 .collect::<Vec<_>>()
125 .join(" ")
126 }
127
128 fn parse_when_clause(&self, when_clause: &str) -> Result<ConditionGroup> {
129 let trimmed = when_clause.trim();
131
132 let clause = if trimmed.starts_with('(') && trimmed.ends_with(')') {
134 let inner = &trimmed[1..trimmed.len() - 1];
136 if self.is_balanced_parentheses(inner) {
137 inner
138 } else {
139 trimmed
140 }
141 } else {
142 trimmed
143 };
144
145 if let Some(parts) = self.split_logical_operator(clause, "||") {
147 return self.parse_or_parts(parts);
148 }
149
150 if let Some(parts) = self.split_logical_operator(clause, "&&") {
152 return self.parse_and_parts(parts);
153 }
154
155 if clause.trim_start().starts_with("!") {
157 return self.parse_not_condition(clause);
158 }
159
160 self.parse_single_condition(clause)
162 }
163
164 fn is_balanced_parentheses(&self, text: &str) -> bool {
165 let mut count = 0;
166 for ch in text.chars() {
167 match ch {
168 '(' => count += 1,
169 ')' => {
170 count -= 1;
171 if count < 0 {
172 return false;
173 }
174 }
175 _ => {}
176 }
177 }
178 count == 0
179 }
180
181 fn split_logical_operator(&self, clause: &str, operator: &str) -> Option<Vec<String>> {
182 let mut parts = Vec::new();
183 let mut current_part = String::new();
184 let mut paren_count = 0;
185 let mut chars = clause.chars().peekable();
186
187 while let Some(ch) = chars.next() {
188 match ch {
189 '(' => {
190 paren_count += 1;
191 current_part.push(ch);
192 }
193 ')' => {
194 paren_count -= 1;
195 current_part.push(ch);
196 }
197 '&' if operator == "&&" && paren_count == 0 => {
198 if chars.peek() == Some(&'&') {
199 chars.next(); parts.push(current_part.trim().to_string());
201 current_part.clear();
202 } else {
203 current_part.push(ch);
204 }
205 }
206 '|' if operator == "||" && paren_count == 0 => {
207 if chars.peek() == Some(&'|') {
208 chars.next(); parts.push(current_part.trim().to_string());
210 current_part.clear();
211 } else {
212 current_part.push(ch);
213 }
214 }
215 _ => {
216 current_part.push(ch);
217 }
218 }
219 }
220
221 if !current_part.trim().is_empty() {
222 parts.push(current_part.trim().to_string());
223 }
224
225 if parts.len() > 1 {
226 Some(parts)
227 } else {
228 None
229 }
230 }
231
232 fn parse_or_parts(&self, parts: Vec<String>) -> Result<ConditionGroup> {
233 let mut conditions = Vec::new();
234 for part in parts {
235 let condition = self.parse_when_clause(&part)?;
236 conditions.push(condition);
237 }
238
239 if conditions.is_empty() {
240 return Err(RuleEngineError::ParseError {
241 message: "No conditions found in OR".to_string(),
242 });
243 }
244
245 let mut iter = conditions.into_iter();
246 let mut result = iter.next().unwrap();
247 for condition in iter {
248 result = ConditionGroup::or(result, condition);
249 }
250
251 Ok(result)
252 }
253
254 fn parse_and_parts(&self, parts: Vec<String>) -> Result<ConditionGroup> {
255 let mut conditions = Vec::new();
256 for part in parts {
257 let condition = self.parse_when_clause(&part)?;
258 conditions.push(condition);
259 }
260
261 if conditions.is_empty() {
262 return Err(RuleEngineError::ParseError {
263 message: "No conditions found in AND".to_string(),
264 });
265 }
266
267 let mut iter = conditions.into_iter();
268 let mut result = iter.next().unwrap();
269 for condition in iter {
270 result = ConditionGroup::and(result, condition);
271 }
272
273 Ok(result)
274 }
275
276 fn parse_not_condition(&self, clause: &str) -> Result<ConditionGroup> {
277 let inner_clause = clause.strip_prefix("!").unwrap().trim();
278 let inner_condition = self.parse_when_clause(inner_clause)?;
279 Ok(ConditionGroup::not(inner_condition))
280 }
281
282 fn parse_single_condition(&self, clause: &str) -> Result<ConditionGroup> {
283 let trimmed_clause = clause.trim();
285 let clause_to_parse = if trimmed_clause.starts_with('(') && trimmed_clause.ends_with(')') {
286 trimmed_clause[1..trimmed_clause.len() - 1].trim()
287 } else {
288 trimmed_clause
289 };
290
291 let typed_object_regex =
293 Regex::new(r#"\$(\w+)\s*:\s*(\w+)\s*\(\s*(.+?)\s*\)"#).map_err(|e| {
294 RuleEngineError::ParseError {
295 message: format!("Typed object regex error: {}", e),
296 }
297 })?;
298
299 if let Some(captures) = typed_object_regex.captures(clause_to_parse) {
300 let _object_name = captures.get(1).unwrap().as_str();
301 let _object_type = captures.get(2).unwrap().as_str();
302 let conditions_str = captures.get(3).unwrap().as_str();
303
304 return self.parse_conditions_within_object(conditions_str);
306 }
307
308 let condition_regex = Regex::new(
311 r#"([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\s*(>=|<=|==|!=|>|<|contains|matches)\s*(.+)"#,
312 )
313 .map_err(|e| RuleEngineError::ParseError {
314 message: format!("Condition regex error: {}", e),
315 })?;
316
317 let captures = condition_regex.captures(clause_to_parse).ok_or_else(|| {
318 RuleEngineError::ParseError {
319 message: format!("Invalid condition format: {}", clause_to_parse),
320 }
321 })?;
322
323 let field = captures.get(1).unwrap().as_str().to_string();
324 let operator_str = captures.get(2).unwrap().as_str();
325 let value_str = captures.get(3).unwrap().as_str().trim();
326
327 let operator =
328 Operator::from_str(operator_str).ok_or_else(|| RuleEngineError::InvalidOperator {
329 operator: operator_str.to_string(),
330 })?;
331
332 let value = self.parse_value(value_str)?;
333
334 let condition = Condition::new(field, operator, value);
335 Ok(ConditionGroup::single(condition))
336 }
337
338 fn parse_conditions_within_object(&self, conditions_str: &str) -> Result<ConditionGroup> {
339 let parts: Vec<&str> = conditions_str.split("&&").collect();
341
342 let mut conditions = Vec::new();
343 for part in parts {
344 let trimmed = part.trim();
345 let condition = self.parse_simple_condition(trimmed)?;
346 conditions.push(condition);
347 }
348
349 if conditions.is_empty() {
351 return Err(RuleEngineError::ParseError {
352 message: "No conditions found".to_string(),
353 });
354 }
355
356 let mut iter = conditions.into_iter();
357 let mut result = iter.next().unwrap();
358 for condition in iter {
359 result = ConditionGroup::and(result, condition);
360 }
361
362 Ok(result)
363 }
364
365 fn parse_simple_condition(&self, clause: &str) -> Result<ConditionGroup> {
366 let condition_regex = Regex::new(r#"(\w+)\s*(>=|<=|==|!=|>|<)\s*(.+)"#).map_err(|e| {
368 RuleEngineError::ParseError {
369 message: format!("Simple condition regex error: {}", e),
370 }
371 })?;
372
373 let captures =
374 condition_regex
375 .captures(clause)
376 .ok_or_else(|| RuleEngineError::ParseError {
377 message: format!("Invalid simple condition format: {}", clause),
378 })?;
379
380 let field = captures.get(1).unwrap().as_str().to_string();
381 let operator_str = captures.get(2).unwrap().as_str();
382 let value_str = captures.get(3).unwrap().as_str().trim();
383
384 let operator =
385 Operator::from_str(operator_str).ok_or_else(|| RuleEngineError::InvalidOperator {
386 operator: operator_str.to_string(),
387 })?;
388
389 let value = self.parse_value(value_str)?;
390
391 let condition = Condition::new(field, operator, value);
392 Ok(ConditionGroup::single(condition))
393 }
394
395 fn parse_value(&self, value_str: &str) -> Result<Value> {
396 let trimmed = value_str.trim();
397
398 if (trimmed.starts_with('"') && trimmed.ends_with('"'))
400 || (trimmed.starts_with('\'') && trimmed.ends_with('\''))
401 {
402 let unquoted = &trimmed[1..trimmed.len() - 1];
403 return Ok(Value::String(unquoted.to_string()));
404 }
405
406 if trimmed.eq_ignore_ascii_case("true") {
408 return Ok(Value::Boolean(true));
409 }
410 if trimmed.eq_ignore_ascii_case("false") {
411 return Ok(Value::Boolean(false));
412 }
413
414 if trimmed.eq_ignore_ascii_case("null") {
416 return Ok(Value::Null);
417 }
418
419 if let Ok(int_val) = trimmed.parse::<i64>() {
421 return Ok(Value::Integer(int_val));
422 }
423
424 if let Ok(float_val) = trimmed.parse::<f64>() {
425 return Ok(Value::Number(float_val));
426 }
427
428 if trimmed.contains('.') {
430 return Ok(Value::String(trimmed.to_string()));
431 }
432
433 Ok(Value::String(trimmed.to_string()))
435 }
436
437 fn parse_then_clause(&self, then_clause: &str) -> Result<Vec<ActionType>> {
438 let statements: Vec<&str> = then_clause
439 .split(';')
440 .map(|s| s.trim())
441 .filter(|s| !s.is_empty())
442 .collect();
443
444 let mut actions = Vec::new();
445
446 for statement in statements {
447 let action = self.parse_action_statement(statement)?;
448 actions.push(action);
449 }
450
451 Ok(actions)
452 }
453
454 fn parse_action_statement(&self, statement: &str) -> Result<ActionType> {
455 let trimmed = statement.trim();
456
457 let method_regex = Regex::new(r#"\$(\w+)\.(\w+)\s*\(([^)]*)\)"#).map_err(|e| {
459 RuleEngineError::ParseError {
460 message: format!("Method regex error: {}", e),
461 }
462 })?;
463
464 if let Some(captures) = method_regex.captures(trimmed) {
465 let object = captures.get(1).unwrap().as_str().to_string();
466 let method = captures.get(2).unwrap().as_str().to_string();
467 let args_str = captures.get(3).unwrap().as_str();
468
469 let args = if args_str.trim().is_empty() {
470 Vec::new()
471 } else {
472 self.parse_method_args(args_str)?
473 };
474
475 return Ok(ActionType::MethodCall {
476 object,
477 method,
478 args,
479 });
480 }
481
482 if let Some(eq_pos) = trimmed.find('=') {
484 let field = trimmed[..eq_pos].trim().to_string();
485 let value_str = trimmed[eq_pos + 1..].trim();
486 let value = self.parse_value(value_str)?;
487
488 return Ok(ActionType::Set { field, value });
489 }
490
491 let func_regex =
493 Regex::new(r#"(\w+)\s*\(\s*(.+?)?\s*\)"#).map_err(|e| RuleEngineError::ParseError {
494 message: format!("Function regex error: {}", e),
495 })?;
496
497 if let Some(captures) = func_regex.captures(trimmed) {
498 let function_name = captures.get(1).unwrap().as_str();
499 let args_str = captures.get(2).map(|m| m.as_str()).unwrap_or("");
500
501 match function_name.to_lowercase().as_str() {
502 "update" => {
503 let object_name = if let Some(stripped) = args_str.strip_prefix('$') {
505 stripped.to_string()
506 } else {
507 args_str.to_string()
508 };
509 Ok(ActionType::Update {
510 object: object_name,
511 })
512 }
513 "set" => {
514 let args = if args_str.is_empty() {
516 Vec::new()
517 } else {
518 args_str
519 .split(',')
520 .map(|arg| self.parse_value(arg.trim()))
521 .collect::<Result<Vec<_>>>()?
522 };
523
524 if args.len() >= 2 {
525 let field = args[0].to_string();
526 let value = args[1].clone();
527 Ok(ActionType::Set { field, value })
528 } else if args.len() == 1 {
529 Ok(ActionType::Set {
531 field: args[0].to_string(),
532 value: Value::Boolean(true),
533 })
534 } else {
535 Ok(ActionType::Custom {
536 action_type: "set".to_string(),
537 params: {
538 let mut params = HashMap::new();
539 params.insert(
540 "args".to_string(),
541 Value::String(args_str.to_string()),
542 );
543 params
544 },
545 })
546 }
547 }
548 "add" => {
549 let value = if args_str.is_empty() {
551 Value::Integer(1) } else {
553 self.parse_value(args_str.trim())?
554 };
555 Ok(ActionType::Custom {
556 action_type: "add".to_string(),
557 params: {
558 let mut params = HashMap::new();
559 params.insert("value".to_string(), value);
560 params
561 },
562 })
563 }
564 "log" => {
565 let message = if args_str.is_empty() {
566 "Log message".to_string()
567 } else {
568 let value = self.parse_value(args_str.trim())?;
569 value.to_string()
570 };
571 Ok(ActionType::Log { message })
572 }
573 _ => {
574 let args = if args_str.is_empty() {
575 Vec::new()
576 } else {
577 args_str
578 .split(',')
579 .map(|arg| self.parse_value(arg.trim()))
580 .collect::<Result<Vec<_>>>()?
581 };
582 Ok(ActionType::Call {
583 function: function_name.to_string(),
584 args,
585 })
586 }
587 }
588 } else {
589 Ok(ActionType::Custom {
591 action_type: "statement".to_string(),
592 params: {
593 let mut params = HashMap::new();
594 params.insert("statement".to_string(), Value::String(trimmed.to_string()));
595 params
596 },
597 })
598 }
599 }
600
601 fn parse_method_args(&self, args_str: &str) -> Result<Vec<Value>> {
602 if args_str.trim().is_empty() {
603 return Ok(Vec::new());
604 }
605
606 let mut args = Vec::new();
608 let parts: Vec<&str> = args_str.split(',').collect();
609
610 for part in parts {
611 let trimmed = part.trim();
612
613 if trimmed.contains('+')
615 || trimmed.contains('-')
616 || trimmed.contains('*')
617 || trimmed.contains('/')
618 {
619 args.push(Value::String(trimmed.to_string()));
621 } else {
622 args.push(self.parse_value(trimmed)?);
623 }
624 }
625
626 Ok(args)
627 }
628}
629
630#[cfg(test)]
631mod tests {
632 use super::GRLParser;
633
634 #[test]
635 fn test_parse_simple_rule() {
636 let grl = r#"
637 rule "CheckAge" salience 10 {
638 when
639 User.Age >= 18
640 then
641 log("User is adult");
642 }
643 "#;
644
645 let rules = GRLParser::parse_rules(grl).unwrap();
646 assert_eq!(rules.len(), 1);
647 let rule = &rules[0];
648 assert_eq!(rule.name, "CheckAge");
649 assert_eq!(rule.salience, 10);
650 assert_eq!(rule.actions.len(), 1);
651 }
652
653 #[test]
654 fn test_parse_complex_condition() {
655 let grl = r#"
656 rule "ComplexRule" {
657 when
658 User.Age >= 18 && User.Country == "US"
659 then
660 User.Qualified = true;
661 }
662 "#;
663
664 let rules = GRLParser::parse_rules(grl).unwrap();
665 assert_eq!(rules.len(), 1);
666 let rule = &rules[0];
667 assert_eq!(rule.name, "ComplexRule");
668 }
669
670 #[test]
671 fn test_parse_new_syntax_with_parentheses() {
672 let grl = r#"
673 rule "Default Rule" salience 10 {
674 when
675 (user.age >= 18)
676 then
677 set(user.status, "approved");
678 }
679 "#;
680
681 let rules = GRLParser::parse_rules(grl).unwrap();
682 assert_eq!(rules.len(), 1);
683 let rule = &rules[0];
684 assert_eq!(rule.name, "Default Rule");
685 assert_eq!(rule.salience, 10);
686 assert_eq!(rule.actions.len(), 1);
687
688 match &rule.actions[0] {
690 crate::types::ActionType::Set { field, value } => {
691 assert_eq!(field, "user.status");
692 assert_eq!(value, &crate::types::Value::String("approved".to_string()));
693 }
694 _ => panic!("Expected Set action, got: {:?}", rule.actions[0]),
695 }
696 }
697
698 #[test]
699 fn test_parse_complex_nested_conditions() {
700 let grl = r#"
701 rule "Complex Business Rule" salience 10 {
702 when
703 (((user.vipStatus == true) && (order.amount > 500)) || ((date.isHoliday == true) && (order.hasCoupon == true)))
704 then
705 apply_discount(20000);
706 }
707 "#;
708
709 let rules = GRLParser::parse_rules(grl).unwrap();
710 assert_eq!(rules.len(), 1);
711 let rule = &rules[0];
712 assert_eq!(rule.name, "Complex Business Rule");
713 assert_eq!(rule.salience, 10);
714 assert_eq!(rule.actions.len(), 1);
715
716 match &rule.actions[0] {
718 crate::types::ActionType::Call { function, args } => {
719 assert_eq!(function, "apply_discount");
720 assert_eq!(args.len(), 1);
721 assert_eq!(args[0], crate::types::Value::Integer(20000));
722 }
723 _ => panic!("Expected Call action, got: {:?}", rule.actions[0]),
724 }
725 }
726}