1use crate::engine::rule::Rule;
2use crate::errors::{Result, RuleEngineError};
3use crate::parser::grl::GRLParser;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub enum ParameterType {
10 String,
12 Number,
14 Boolean,
16 Array,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ParameterDef {
23 pub name: String,
25 pub param_type: ParameterType,
27 pub default_value: Option<String>,
29 pub description: Option<String>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct RuleTemplate {
36 pub name: String,
38 pub description: Option<String>,
40 pub parameters: Vec<ParameterDef>,
42 pub condition_template: String,
44 pub action_template: String,
46 pub salience: Option<i32>,
48}
49
50pub struct TemplateInstance {
52 template: RuleTemplate,
53 rule_name: String,
54 parameter_values: HashMap<String, String>,
55}
56
57pub struct TemplateManager {
59 templates: HashMap<String, RuleTemplate>,
60}
61
62impl RuleTemplate {
63 pub fn new(name: &str) -> Self {
65 Self {
66 name: name.to_string(),
67 description: None,
68 parameters: Vec::new(),
69 condition_template: String::new(),
70 action_template: String::new(),
71 salience: None,
72 }
73 }
74
75 pub fn with_parameter(mut self, name: &str, param_type: ParameterType) -> Self {
77 self.parameters.push(ParameterDef {
78 name: name.to_string(),
79 param_type,
80 default_value: None,
81 description: None,
82 });
83 self
84 }
85
86 pub fn with_condition(mut self, condition: &str) -> Self {
88 self.condition_template = condition.to_string();
89 self
90 }
91
92 pub fn with_action(mut self, action: &str) -> Self {
94 self.action_template = action.to_string();
95 self
96 }
97
98 pub fn with_salience(mut self, salience: i32) -> Self {
100 self.salience = Some(salience);
101 self
102 }
103
104 pub fn with_description(mut self, description: &str) -> Self {
106 self.description = Some(description.to_string());
107 self
108 }
109
110 pub fn instantiate(&self, rule_name: &str) -> TemplateInstance {
112 TemplateInstance {
113 template: self.clone(),
114 rule_name: rule_name.to_string(),
115 parameter_values: HashMap::new(),
116 }
117 }
118
119 pub fn validate_parameters(&self, params: &HashMap<String, String>) -> Result<()> {
121 for param_def in &self.parameters {
122 if !params.contains_key(¶m_def.name) && param_def.default_value.is_none() {
123 return Err(RuleEngineError::ParseError {
124 message: format!("Missing required parameter: {}", param_def.name),
125 });
126 }
127 }
128 Ok(())
129 }
130
131 pub fn substitute_placeholders(&self, text: &str, params: &HashMap<String, String>) -> String {
133 let mut result = text.to_string();
134
135 for (key, value) in params {
136 let placeholder = format!("{{{{{}}}}}", key);
137 result = result.replace(&placeholder, value);
138 }
139
140 for param_def in &self.parameters {
142 if !params.contains_key(¶m_def.name) {
143 if let Some(default_value) = ¶m_def.default_value {
144 let placeholder = format!("{{{{{}}}}}", param_def.name);
145 result = result.replace(&placeholder, default_value);
146 }
147 }
148 }
149
150 result
151 }
152}
153
154impl TemplateInstance {
155 pub fn with_param(mut self, name: &str, value: impl ToString) -> Self {
157 self.parameter_values
158 .insert(name.to_string(), value.to_string());
159 self
160 }
161
162 pub fn build(self) -> Result<Rule> {
164 self.template.validate_parameters(&self.parameter_values)?;
166
167 let condition = self
169 .template
170 .substitute_placeholders(&self.template.condition_template, &self.parameter_values);
171 let action = self
172 .template
173 .substitute_placeholders(&self.template.action_template, &self.parameter_values);
174
175 let grl_rule = if let Some(salience) = self.template.salience {
177 format!(
178 r#"rule "{}" salience {} {{
179when
180{}
181then
182{};
183}}"#,
184 self.rule_name, salience, condition, action
185 )
186 } else {
187 format!(
188 r#"rule "{}" {{
189when
190{}
191then
192{};
193}}"#,
194 self.rule_name, condition, action
195 )
196 };
197
198 let rules = GRLParser::parse_rules(&grl_rule)?;
200
201 if rules.is_empty() {
202 return Err(RuleEngineError::ParseError {
203 message: "Failed to generate rule from template".to_string(),
204 });
205 }
206
207 Ok(rules.into_iter().next().unwrap())
208 }
209}
210
211impl TemplateManager {
212 pub fn new() -> Self {
214 Self {
215 templates: HashMap::new(),
216 }
217 }
218
219 pub fn register_template(&mut self, template: RuleTemplate) {
221 self.templates.insert(template.name.clone(), template);
222 }
223
224 pub fn get_template(&self, name: &str) -> Option<&RuleTemplate> {
226 self.templates.get(name)
227 }
228
229 pub fn generate_rules(
231 &self,
232 template_name: &str,
233 rule_configs: Vec<(String, HashMap<String, String>)>,
234 ) -> Result<Vec<Rule>> {
235 let template =
236 self.get_template(template_name)
237 .ok_or_else(|| RuleEngineError::ParseError {
238 message: format!("Template not found: {}", template_name),
239 })?;
240
241 let mut rules = Vec::new();
242
243 for (rule_name, params) in rule_configs {
244 let mut instance = template.instantiate(&rule_name);
245 instance.parameter_values = params;
246
247 rules.push(instance.build()?);
248 }
249
250 Ok(rules)
251 }
252
253 pub fn load_from_json(&mut self, json_content: &str) -> Result<()> {
255 let templates: Vec<RuleTemplate> =
256 serde_json::from_str(json_content).map_err(|e| RuleEngineError::ParseError {
257 message: format!("Failed to parse template JSON: {}", e),
258 })?;
259
260 for template in templates {
261 self.register_template(template);
262 }
263
264 Ok(())
265 }
266
267 pub fn save_to_json(&self) -> Result<String> {
269 let templates: Vec<&RuleTemplate> = self.templates.values().collect();
270 serde_json::to_string_pretty(&templates).map_err(|e| RuleEngineError::ParseError {
271 message: format!("Failed to serialize templates: {}", e),
272 })
273 }
274
275 pub fn list_templates(&self) -> Vec<&str> {
277 self.templates.keys().map(|s| s.as_str()).collect()
278 }
279}
280
281impl Default for TemplateManager {
282 fn default() -> Self {
283 Self::new()
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_template_creation() {
293 let template = RuleTemplate::new("VIPCheck")
294 .with_parameter("country", ParameterType::String)
295 .with_parameter("threshold", ParameterType::Number)
296 .with_condition(
297 "User.Country == \"{{country}}\" && User.SpendingTotal >= {{threshold}}",
298 )
299 .with_action("User.setIsVIP(true)")
300 .with_salience(10);
301
302 assert_eq!(template.name, "VIPCheck");
303 assert_eq!(template.parameters.len(), 2);
304 assert_eq!(template.salience, Some(10));
305 }
306
307 #[test]
308 fn test_template_instantiation() {
309 let template = RuleTemplate::new("VIPCheck")
310 .with_parameter("country", ParameterType::String)
311 .with_parameter("threshold", ParameterType::Number)
312 .with_condition(
313 "User.Country == \"{{country}}\" && User.SpendingTotal >= {{threshold}}",
314 )
315 .with_action("User.setIsVIP(true)");
316
317 let rule = template
318 .instantiate("VIPCheck_US")
319 .with_param("country", "US")
320 .with_param("threshold", "1000")
321 .build()
322 .unwrap();
323
324 assert_eq!(rule.name, "VIPCheck_US");
325 }
326
327 #[test]
328 fn test_template_manager() {
329 let mut manager = TemplateManager::new();
330
331 let template = RuleTemplate::new("TestTemplate")
332 .with_parameter("value", ParameterType::String)
333 .with_condition("User.Field == \"{{value}}\"")
334 .with_action("User.setResult(true)");
335
336 manager.register_template(template);
337
338 assert!(manager.get_template("TestTemplate").is_some());
339 assert_eq!(manager.list_templates().len(), 1);
340 }
341}