rust_rule_engine/parser/
grl_parser.rs1use crate::engine::rule::{Condition, ConditionGroup, Rule};
2use crate::errors::{Result, RuleEngineError};
3use crate::types::{ActionType, Operator, Value};
4
5pub struct GRLParser;
7
8impl GRLParser {
9 pub fn parse_rules(content: &str) -> Result<Vec<Rule>> {
11 let mut rules = Vec::new();
12
13 let rule_blocks = Self::extract_rule_blocks(content)?;
15
16 for block in rule_blocks {
17 let rule = Self::parse_single_rule(&block)?;
18 rules.push(rule);
19 }
20
21 Ok(rules)
22 }
23
24 fn extract_rule_blocks(content: &str) -> Result<Vec<String>> {
26 let mut blocks = Vec::new();
27 let mut current_block = String::new();
28 let mut brace_count = 0;
29 let mut in_rule = false;
30
31 for line in content.lines() {
32 let line = line.trim();
33
34 if line.starts_with("rule ") {
35 in_rule = true;
36 current_block.clear();
37 current_block.push_str(line);
38 current_block.push('\n');
39
40 for ch in line.chars() {
42 match ch {
43 '{' => brace_count += 1,
44 '}' => {
45 brace_count -= 1;
46 if brace_count == 0 {
47 blocks.push(current_block.clone());
48 current_block.clear();
49 in_rule = false;
50 break;
51 }
52 }
53 _ => {}
54 }
55 }
56 continue;
57 }
58
59 if in_rule {
60 current_block.push_str(line);
61 current_block.push('\n');
62
63 for ch in line.chars() {
64 match ch {
65 '{' => brace_count += 1,
66 '}' => {
67 brace_count -= 1;
68 if brace_count == 0 {
69 blocks.push(current_block.clone());
70 current_block.clear();
71 in_rule = false;
72 break;
73 }
74 }
75 _ => {}
76 }
77 }
78 }
79 }
80
81 Ok(blocks)
82 }
83
84 fn parse_single_rule(block: &str) -> Result<Rule> {
86 let lines: Vec<&str> = block.lines().collect();
87
88 let (name, salience) = Self::parse_rule_header(lines[0])?;
90
91 let mut when_section = Vec::new();
93 let mut then_section = Vec::new();
94 let mut current_section = "";
95
96 for line in lines.iter().skip(1) {
97 let line = line.trim();
98
99 if line == "when" {
100 current_section = "when";
101 continue;
102 } else if line == "then" {
103 current_section = "then";
104 continue;
105 } else if line == "}" || line.is_empty() {
106 continue;
107 }
108
109 match current_section {
110 "when" => when_section.push(line),
111 "then" => then_section.push(line),
112 _ => {}
113 }
114 }
115
116 let conditions = Self::parse_conditions(&when_section)?;
118
119 let actions = Self::parse_actions(&then_section)?;
121
122 Ok(Rule::new(name, conditions, actions).with_salience(salience))
123 }
124
125 fn parse_rule_header(header: &str) -> Result<(String, i32)> {
127 let parts: Vec<&str> = header.split_whitespace().collect();
129
130 let name = if parts.len() >= 2 {
131 parts[1].trim_matches('"').to_string()
132 } else {
133 return Err(RuleEngineError::ParseError {
134 message: "Invalid rule header".to_string(),
135 });
136 };
137
138 let salience = if let Some(sal_idx) = parts.iter().position(|&x| x == "salience") {
139 if sal_idx + 1 < parts.len() {
140 parts[sal_idx + 1].parse::<i32>().unwrap_or(0)
141 } else {
142 0
143 }
144 } else {
145 0
146 };
147
148 Ok((name, salience))
149 }
150
151 fn parse_conditions(when_lines: &[&str]) -> Result<ConditionGroup> {
153 if when_lines.is_empty() {
154 return Err(RuleEngineError::ParseError {
155 message: "No conditions found".to_string(),
156 });
157 }
158
159 let condition_line = when_lines.join(" ");
161
162 if let Some((field, op, value)) = Self::parse_simple_condition(&condition_line) {
164 let condition = Condition::new(field, op, value);
165 return Ok(ConditionGroup::single(condition));
166 }
167
168 Ok(ConditionGroup::single(Condition::new(
170 "Default.Field".to_string(),
171 Operator::Equal,
172 Value::Boolean(true),
173 )))
174 }
175
176 fn parse_simple_condition(condition: &str) -> Option<(String, Operator, Value)> {
178 let condition = condition.trim();
179
180 if let Some(pos) = condition.find(" >= ") {
182 let field = condition[..pos].trim().to_string();
183 let value_str = condition[pos + 4..].trim();
184 let value = Self::parse_value(value_str)?;
185 return Some((field, Operator::GreaterThanOrEqual, value));
186 }
187
188 if let Some(pos) = condition.find(" > ") {
189 let field = condition[..pos].trim().to_string();
190 let value_str = condition[pos + 3..].trim();
191 let value = Self::parse_value(value_str)?;
192 return Some((field, Operator::GreaterThan, value));
193 }
194
195 if let Some(pos) = condition.find(" <= ") {
196 let field = condition[..pos].trim().to_string();
197 let value_str = condition[pos + 4..].trim();
198 let value = Self::parse_value(value_str)?;
199 return Some((field, Operator::LessThanOrEqual, value));
200 }
201
202 if let Some(pos) = condition.find(" < ") {
203 let field = condition[..pos].trim().to_string();
204 let value_str = condition[pos + 3..].trim();
205 let value = Self::parse_value(value_str)?;
206 return Some((field, Operator::LessThan, value));
207 }
208
209 if let Some(pos) = condition.find(" == ") {
210 let field = condition[..pos].trim().to_string();
211 let value_str = condition[pos + 4..].trim();
212 let value = Self::parse_value(value_str)?;
213 return Some((field, Operator::Equal, value));
214 }
215
216 if let Some(pos) = condition.find(" != ") {
217 let field = condition[..pos].trim().to_string();
218 let value_str = condition[pos + 4..].trim();
219 let value = Self::parse_value(value_str)?;
220 return Some((field, Operator::NotEqual, value));
221 }
222
223 None
224 }
225
226 fn parse_value(value_str: &str) -> Option<Value> {
228 let value_str = value_str.trim();
229
230 if value_str == "true" {
232 return Some(Value::Boolean(true));
233 }
234 if value_str == "false" {
235 return Some(Value::Boolean(false));
236 }
237
238 if value_str.starts_with('"') && value_str.ends_with('"') {
240 let s = value_str[1..value_str.len() - 1].to_string();
241 return Some(Value::String(s));
242 }
243
244 if let Ok(f) = value_str.parse::<f64>() {
246 return Some(Value::Number(f));
247 }
248
249 if let Ok(i) = value_str.parse::<i64>() {
251 return Some(Value::Integer(i));
252 }
253
254 Some(Value::String(value_str.to_string()))
256 }
257
258 fn parse_actions(then_lines: &[&str]) -> Result<Vec<ActionType>> {
260 let mut actions = Vec::new();
261
262 for line in then_lines {
263 let line = line.trim().trim_end_matches(';');
264
265 if line.is_empty() {
266 continue;
267 }
268
269 if let Some(action) = Self::parse_action_line(line) {
271 actions.push(action);
272 }
273 }
274
275 Ok(actions)
276 }
277
278 fn parse_action_line(line: &str) -> Option<ActionType> {
280 if let Some(dot_pos) = line.find('.') {
282 if let Some(paren_pos) = line.find('(') {
283 if dot_pos < paren_pos {
284 let object = line[..dot_pos].trim().to_string();
285 let method = line[dot_pos + 1..paren_pos].trim().to_string();
286
287 if let Some(close_paren) = line.rfind(')') {
289 let args_str = &line[paren_pos + 1..close_paren];
290 let args = Self::parse_function_args(args_str);
291
292 return Some(ActionType::MethodCall {
293 object,
294 method,
295 args,
296 });
297 }
298 }
299 }
300 }
301
302 if let Some(paren_pos) = line.find('(') {
304 let function = line[..paren_pos].trim().to_string();
305 if let Some(close_paren) = line.rfind(')') {
306 let args_str = &line[paren_pos + 1..close_paren];
307 let args = Self::parse_function_args(args_str);
308
309 return Some(ActionType::Call { function, args });
310 }
311 }
312
313 None
314 }
315
316 fn parse_function_args(args_str: &str) -> Vec<Value> {
318 if args_str.trim().is_empty() {
319 return Vec::new();
320 }
321
322 args_str
323 .split(',')
324 .map(|arg| {
325 let arg = arg.trim();
326 Self::parse_value(arg).unwrap_or(Value::String(arg.to_string()))
327 })
328 .collect()
329 }
330}