1use crate::errors::{Result, RuleEngineError};
7use crate::rete::facts::{FactValue, TypedFacts};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, PartialEq)]
12pub struct FieldDef {
13 pub name: String,
14 pub field_type: FieldType,
15 pub default_value: Option<FactValue>,
16 pub required: bool,
17}
18
19#[derive(Debug, Clone, PartialEq)]
21pub enum FieldType {
22 String,
23 Integer,
24 Float,
25 Boolean,
26 Array(Box<FieldType>),
27 Any,
28}
29
30impl FieldType {
31 pub fn matches(&self, value: &FactValue) -> bool {
33 match (self, value) {
34 (FieldType::String, FactValue::String(_)) => true,
35 (FieldType::Integer, FactValue::Integer(_)) => true,
36 (FieldType::Float, FactValue::Float(_)) => true,
37 (FieldType::Boolean, FactValue::Boolean(_)) => true,
38 (FieldType::Array(inner), FactValue::Array(arr)) => {
39 arr.iter().all(|v| inner.matches(v))
41 }
42 (FieldType::Any, _) => true,
43 _ => false,
44 }
45 }
46
47 pub fn default_value(&self) -> FactValue {
49 match self {
50 FieldType::String => FactValue::String(String::new()),
51 FieldType::Integer => FactValue::Integer(0),
52 FieldType::Float => FactValue::Float(0.0),
53 FieldType::Boolean => FactValue::Boolean(false),
54 FieldType::Array(_) => FactValue::Array(Vec::new()),
55 FieldType::Any => FactValue::Null,
56 }
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct Template {
63 pub name: String,
64 pub fields: Vec<FieldDef>,
65 field_map: HashMap<String, usize>,
66}
67
68impl Template {
69 pub fn new(name: impl Into<String>) -> Self {
71 Self {
72 name: name.into(),
73 fields: Vec::new(),
74 field_map: HashMap::new(),
75 }
76 }
77
78 pub fn add_field(&mut self, field: FieldDef) -> &mut Self {
80 let idx = self.fields.len();
81 self.field_map.insert(field.name.clone(), idx);
82 self.fields.push(field);
83 self
84 }
85
86 pub fn validate(&self, facts: &TypedFacts) -> Result<()> {
88 for field in &self.fields {
90 let value = facts.get(&field.name);
91
92 if field.required && value.is_none() {
93 return Err(RuleEngineError::EvaluationError {
94 message: format!(
95 "Required field '{}' missing in template '{}'",
96 field.name, self.name
97 ),
98 });
99 }
100
101 if let Some(val) = value {
103 if !field.field_type.matches(val) {
104 return Err(RuleEngineError::EvaluationError {
105 message: format!(
106 "Field '{}' has wrong type. Expected {:?}, got {:?}",
107 field.name, field.field_type, val
108 ),
109 });
110 }
111 }
112 }
113
114 Ok(())
115 }
116
117 pub fn create_instance(&self) -> TypedFacts {
119 let mut facts = TypedFacts::new();
120
121 for field in &self.fields {
122 let value = field
123 .default_value
124 .clone()
125 .unwrap_or_else(|| field.field_type.default_value());
126 facts.set(&field.name, value);
127 }
128
129 facts
130 }
131
132 pub fn get_field(&self, name: &str) -> Option<&FieldDef> {
134 self.field_map
135 .get(name)
136 .and_then(|idx| self.fields.get(*idx))
137 }
138}
139
140pub struct TemplateBuilder {
142 template: Template,
143}
144
145impl TemplateBuilder {
146 pub fn new(name: impl Into<String>) -> Self {
148 Self {
149 template: Template::new(name),
150 }
151 }
152
153 pub fn string_field(mut self, name: impl Into<String>) -> Self {
155 self.template.add_field(FieldDef {
156 name: name.into(),
157 field_type: FieldType::String,
158 default_value: None,
159 required: false,
160 });
161 self
162 }
163
164 pub fn required_string(mut self, name: impl Into<String>) -> Self {
166 self.template.add_field(FieldDef {
167 name: name.into(),
168 field_type: FieldType::String,
169 default_value: None,
170 required: true,
171 });
172 self
173 }
174
175 pub fn integer_field(mut self, name: impl Into<String>) -> Self {
177 self.template.add_field(FieldDef {
178 name: name.into(),
179 field_type: FieldType::Integer,
180 default_value: None,
181 required: false,
182 });
183 self
184 }
185
186 pub fn float_field(mut self, name: impl Into<String>) -> Self {
188 self.template.add_field(FieldDef {
189 name: name.into(),
190 field_type: FieldType::Float,
191 default_value: None,
192 required: false,
193 });
194 self
195 }
196
197 pub fn boolean_field(mut self, name: impl Into<String>) -> Self {
199 self.template.add_field(FieldDef {
200 name: name.into(),
201 field_type: FieldType::Boolean,
202 default_value: None,
203 required: false,
204 });
205 self
206 }
207
208 pub fn field_with_default(
210 mut self,
211 name: impl Into<String>,
212 field_type: FieldType,
213 default: FactValue,
214 ) -> Self {
215 self.template.add_field(FieldDef {
216 name: name.into(),
217 field_type,
218 default_value: Some(default),
219 required: false,
220 });
221 self
222 }
223
224 pub fn array_field(mut self, name: impl Into<String>, element_type: FieldType) -> Self {
226 self.template.add_field(FieldDef {
227 name: name.into(),
228 field_type: FieldType::Array(Box::new(element_type)),
229 default_value: None,
230 required: false,
231 });
232 self
233 }
234
235 pub fn multislot_field(self, name: impl Into<String>, element_type: FieldType) -> Self {
256 self.array_field(name, element_type)
257 }
258
259 pub fn required_array_field(
261 mut self,
262 name: impl Into<String>,
263 element_type: FieldType,
264 ) -> Self {
265 self.template.add_field(FieldDef {
266 name: name.into(),
267 field_type: FieldType::Array(Box::new(element_type)),
268 default_value: None,
269 required: true,
270 });
271 self
272 }
273
274 pub fn required_multislot_field(
276 self,
277 name: impl Into<String>,
278 element_type: FieldType,
279 ) -> Self {
280 self.required_array_field(name, element_type)
281 }
282
283 pub fn build(self) -> Template {
285 self.template
286 }
287}
288
289pub struct TemplateRegistry {
291 templates: HashMap<String, Template>,
292}
293
294impl TemplateRegistry {
295 pub fn new() -> Self {
297 Self {
298 templates: HashMap::new(),
299 }
300 }
301
302 pub fn register(&mut self, template: Template) {
304 self.templates.insert(template.name.clone(), template);
305 }
306
307 pub fn get(&self, name: &str) -> Option<&Template> {
309 self.templates.get(name)
310 }
311
312 pub fn create_instance(&self, template_name: &str) -> Result<TypedFacts> {
314 let template = self
315 .get(template_name)
316 .ok_or_else(|| RuleEngineError::EvaluationError {
317 message: format!("Template '{}' not found", template_name),
318 })?;
319
320 Ok(template.create_instance())
321 }
322
323 pub fn validate(&self, template_name: &str, facts: &TypedFacts) -> Result<()> {
325 let template = self
326 .get(template_name)
327 .ok_or_else(|| RuleEngineError::EvaluationError {
328 message: format!("Template '{}' not found", template_name),
329 })?;
330
331 template.validate(facts)
332 }
333
334 pub fn list_templates(&self) -> Vec<&str> {
336 self.templates.keys().map(|s| s.as_str()).collect()
337 }
338}
339
340impl Default for TemplateRegistry {
341 fn default() -> Self {
342 Self::new()
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn test_template_builder() {
352 let template = TemplateBuilder::new("Person")
353 .required_string("name")
354 .integer_field("age")
355 .boolean_field("is_adult")
356 .build();
357
358 assert_eq!(template.name, "Person");
359 assert_eq!(template.fields.len(), 3);
360 assert!(template.get_field("name").unwrap().required);
361 }
362
363 #[test]
364 fn test_create_instance() {
365 let template = TemplateBuilder::new("Person")
366 .string_field("name")
367 .integer_field("age")
368 .build();
369
370 let instance = template.create_instance();
371 assert_eq!(
372 instance.get("name"),
373 Some(&FactValue::String(String::new()))
374 );
375 assert_eq!(instance.get("age"), Some(&FactValue::Integer(0)));
376 }
377
378 #[test]
379 fn test_validation_success() {
380 let template = TemplateBuilder::new("Person")
381 .required_string("name")
382 .integer_field("age")
383 .build();
384
385 let mut facts = TypedFacts::new();
386 facts.set("name", FactValue::String("Alice".to_string()));
387 facts.set("age", FactValue::Integer(30));
388
389 assert!(template.validate(&facts).is_ok());
390 }
391
392 #[test]
393 fn test_validation_missing_required() {
394 let template = TemplateBuilder::new("Person")
395 .required_string("name")
396 .integer_field("age")
397 .build();
398
399 let mut facts = TypedFacts::new();
400 facts.set("age", FactValue::Integer(30));
401
402 assert!(template.validate(&facts).is_err());
403 }
404
405 #[test]
406 fn test_validation_wrong_type() {
407 let template = TemplateBuilder::new("Person")
408 .string_field("name")
409 .integer_field("age")
410 .build();
411
412 let mut facts = TypedFacts::new();
413 facts.set("name", FactValue::String("Alice".to_string()));
414 facts.set("age", FactValue::String("thirty".to_string())); assert!(template.validate(&facts).is_err());
417 }
418
419 #[test]
420 fn test_template_registry() {
421 let mut registry = TemplateRegistry::new();
422
423 let template = TemplateBuilder::new("Order")
424 .required_string("order_id")
425 .float_field("amount")
426 .build();
427
428 registry.register(template);
429
430 assert!(registry.get("Order").is_some());
431 assert!(registry.create_instance("Order").is_ok());
432 assert_eq!(registry.list_templates(), vec!["Order"]);
433 }
434
435 #[test]
436 fn test_array_field() {
437 let template = TemplateBuilder::new("ShoppingCart")
438 .array_field("items", FieldType::String)
439 .build();
440
441 let mut facts = TypedFacts::new();
442 facts.set(
443 "items",
444 FactValue::Array(vec![
445 FactValue::String("item1".to_string()),
446 FactValue::String("item2".to_string()),
447 ]),
448 );
449
450 assert!(template.validate(&facts).is_ok());
451 }
452
453 #[test]
454 fn test_field_with_default() {
455 let template = TemplateBuilder::new("Config")
456 .field_with_default("timeout", FieldType::Integer, FactValue::Integer(30))
457 .build();
458
459 let instance = template.create_instance();
460 assert_eq!(instance.get("timeout"), Some(&FactValue::Integer(30)));
461 }
462}