1use crate::error::{LearningError, Result};
3use crate::models::Rule;
4use serde_json::{json, Value};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone)]
9pub struct GenerationContext {
10 pub generation_type: String,
12 pub language: String,
14 pub input: String,
16 pub metadata: HashMap<String, Value>,
18}
19
20impl GenerationContext {
21 pub fn new(generation_type: String, language: String, input: String) -> Self {
23 Self {
24 generation_type,
25 language,
26 input,
27 metadata: HashMap::new(),
28 }
29 }
30
31 pub fn with_metadata(mut self, key: String, value: Value) -> Self {
33 self.metadata.insert(key, value);
34 self
35 }
36
37 pub fn to_json(&self) -> Value {
39 json!({
40 "generation_type": self.generation_type,
41 "language": self.language,
42 "input_length": self.input.len(),
43 "metadata": self.metadata,
44 })
45 }
46}
47
48#[derive(Debug, Clone)]
50pub struct RuleApplicationResult {
51 pub rule: Rule,
53 pub matched: bool,
55 pub action: Option<String>,
57 pub confidence: f32,
59 pub details: HashMap<String, Value>,
61}
62
63impl RuleApplicationResult {
64 pub fn new(rule: Rule, matched: bool) -> Self {
66 Self {
67 confidence: rule.confidence,
68 action: if matched { Some(rule.action.clone()) } else { None },
69 rule,
70 matched,
71 details: HashMap::new(),
72 }
73 }
74
75 pub fn with_detail(mut self, key: String, value: Value) -> Self {
77 self.details.insert(key, value);
78 self
79 }
80}
81
82pub struct RuleApplicationEngine;
84
85impl RuleApplicationEngine {
86 pub fn matches_pattern(rule: &Rule, context: &GenerationContext) -> bool {
88 if let Ok(pattern_value) = serde_json::from_str::<Value>(&rule.pattern) {
90 Self::pattern_matches(&pattern_value, context)
91 } else {
92 rule.pattern.contains(&context.generation_type)
94 || rule.pattern.contains(&context.language)
95 }
96 }
97
98 fn pattern_matches(pattern: &Value, context: &GenerationContext) -> bool {
100 match pattern {
101 Value::Object(obj) => {
102 for (key, value) in obj {
104 match key.as_str() {
105 "generation_type" => {
106 if let Value::String(expected) = value {
107 if context.generation_type != *expected {
108 return false;
109 }
110 }
111 }
112 "language" => {
113 if let Value::String(expected) = value {
114 if context.language != *expected {
115 return false;
116 }
117 }
118 }
119 "metadata" => {
120 if let Value::Object(expected_meta) = value {
121 for (meta_key, meta_value) in expected_meta {
122 if let Some(actual_value) = context.metadata.get(meta_key) {
123 if actual_value != meta_value {
124 return false;
125 }
126 } else {
127 return false;
128 }
129 }
130 }
131 }
132 _ => {
133 }
135 }
136 }
137 true
138 }
139 Value::String(pattern_str) => {
140 pattern_str.contains(&context.generation_type)
142 || pattern_str.contains(&context.language)
143 }
144 _ => false,
145 }
146 }
147
148 pub fn apply_rule(rule: &Rule, context: &GenerationContext) -> RuleApplicationResult {
150 let matched = Self::matches_pattern(rule, context);
151 let mut result = RuleApplicationResult::new(rule.clone(), matched);
152
153 if matched {
154 result = result.with_detail(
155 "applied_at".to_string(),
156 json!(chrono::Utc::now().to_rfc3339()),
157 );
158 }
159
160 result
161 }
162
163 pub fn apply_rules(
165 rules: &[Rule],
166 context: &GenerationContext,
167 ) -> Vec<RuleApplicationResult> {
168 rules
169 .iter()
170 .map(|rule| Self::apply_rule(rule, context))
171 .collect()
172 }
173
174 pub fn apply_rules_with_precedence(
176 rules: &[Rule],
177 context: &GenerationContext,
178 ) -> Option<RuleApplicationResult> {
179 let results = Self::apply_rules(rules, context);
180 results
181 .into_iter()
182 .filter(|r| r.matched)
183 .max_by(|a, b| {
184 a.confidence
185 .partial_cmp(&b.confidence)
186 .unwrap_or(std::cmp::Ordering::Equal)
187 })
188 }
189
190 pub fn chain_rules(
192 rules: &[Rule],
193 context: &GenerationContext,
194 ) -> Result<Vec<RuleApplicationResult>> {
195 let mut results = Vec::new();
196 let mut current_context = context.clone();
197
198 for rule in rules {
199 let result = Self::apply_rule(rule, ¤t_context);
200
201 if result.matched {
202 if let Some(action) = &result.action {
204 current_context = current_context.with_metadata(
205 "last_action".to_string(),
206 json!(action),
207 );
208 }
209 }
210
211 results.push(result);
212 }
213
214 Ok(results)
215 }
216
217 pub fn compose_rules(
219 rules: &[Rule],
220 context: &GenerationContext,
221 ) -> Result<Option<String>> {
222 let results = Self::apply_rules(rules, context);
223 let matched_actions: Vec<String> = results
224 .iter()
225 .filter(|r| r.matched)
226 .filter_map(|r| r.action.clone())
227 .collect();
228
229 if matched_actions.is_empty() {
230 Ok(None)
231 } else {
232 Ok(Some(matched_actions.join("\n")))
234 }
235 }
236
237 pub fn validate_rule_application(rule: &Rule, context: &GenerationContext) -> Result<()> {
239 if let Err(_) = serde_json::from_str::<Value>(&rule.pattern) {
241 if rule.pattern.is_empty() {
243 return Err(LearningError::RuleApplicationFailed(
244 "Rule pattern cannot be empty".to_string(),
245 ));
246 }
247 }
248
249 if rule.action.is_empty() {
251 return Err(LearningError::RuleApplicationFailed(
252 "Rule action cannot be empty".to_string(),
253 ));
254 }
255
256 if context.generation_type.is_empty() {
258 return Err(LearningError::RuleApplicationFailed(
259 "Generation context type cannot be empty".to_string(),
260 ));
261 }
262
263 if context.language.is_empty() {
264 return Err(LearningError::RuleApplicationFailed(
265 "Generation context language cannot be empty".to_string(),
266 ));
267 }
268
269 Ok(())
270 }
271
272 pub fn get_matching_rules(rules: &[Rule], context: &GenerationContext) -> Vec<Rule> {
274 rules
275 .iter()
276 .filter(|rule| Self::matches_pattern(rule, context))
277 .cloned()
278 .collect()
279 }
280
281 pub fn get_matching_rules_sorted(rules: &[Rule], context: &GenerationContext) -> Vec<Rule> {
283 let mut matching = Self::get_matching_rules(rules, context);
284 matching.sort_by(|a, b| {
285 b.confidence
286 .partial_cmp(&a.confidence)
287 .unwrap_or(std::cmp::Ordering::Equal)
288 });
289 matching
290 }
291
292 pub fn get_matching_rules_by_usage(rules: &[Rule], context: &GenerationContext) -> Vec<Rule> {
294 let mut matching = Self::get_matching_rules(rules, context);
295 matching.sort_by(|a, b| b.usage_count.cmp(&a.usage_count));
296 matching
297 }
298
299 pub fn get_matching_rules_by_success(rules: &[Rule], context: &GenerationContext) -> Vec<Rule> {
301 let mut matching = Self::get_matching_rules(rules, context);
302 matching.sort_by(|a, b| {
303 b.success_rate
304 .partial_cmp(&a.success_rate)
305 .unwrap_or(std::cmp::Ordering::Equal)
306 });
307 matching
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use super::*;
314
315 #[test]
316 fn test_generation_context_creation() {
317 let context = GenerationContext::new(
318 "function".to_string(),
319 "rust".to_string(),
320 "fn test() {}".to_string(),
321 );
322
323 assert_eq!(context.generation_type, "function");
324 assert_eq!(context.language, "rust");
325 assert_eq!(context.input, "fn test() {}");
326 }
327
328 #[test]
329 fn test_generation_context_with_metadata() {
330 let context = GenerationContext::new(
331 "function".to_string(),
332 "rust".to_string(),
333 "fn test() {}".to_string(),
334 )
335 .with_metadata("style".to_string(), json!("async"));
336
337 assert_eq!(context.metadata.get("style").unwrap(), &json!("async"));
338 }
339
340 #[test]
341 fn test_rule_application_result() {
342 let rule = Rule::new(
343 crate::models::RuleScope::Session,
344 "function".to_string(),
345 "add_documentation".to_string(),
346 crate::models::RuleSource::Learned,
347 );
348
349 let result = RuleApplicationResult::new(rule.clone(), true);
350 assert!(result.matched);
351 assert_eq!(result.action, Some("add_documentation".to_string()));
352 }
353
354 #[test]
355 fn test_simple_pattern_matching() {
356 let rule = Rule::new(
357 crate::models::RuleScope::Session,
358 "function".to_string(),
359 "add_documentation".to_string(),
360 crate::models::RuleSource::Learned,
361 );
362
363 let context = GenerationContext::new(
364 "function".to_string(),
365 "rust".to_string(),
366 "fn test() {}".to_string(),
367 );
368
369 assert!(RuleApplicationEngine::matches_pattern(&rule, &context));
370 }
371
372 #[test]
373 fn test_pattern_not_matching() {
374 let rule = Rule::new(
375 crate::models::RuleScope::Session,
376 "class".to_string(),
377 "add_documentation".to_string(),
378 crate::models::RuleSource::Learned,
379 );
380
381 let context = GenerationContext::new(
382 "function".to_string(),
383 "rust".to_string(),
384 "fn test() {}".to_string(),
385 );
386
387 assert!(!RuleApplicationEngine::matches_pattern(&rule, &context));
388 }
389
390 #[test]
391 fn test_apply_single_rule() {
392 let rule = Rule::new(
393 crate::models::RuleScope::Session,
394 "function".to_string(),
395 "add_documentation".to_string(),
396 crate::models::RuleSource::Learned,
397 );
398
399 let context = GenerationContext::new(
400 "function".to_string(),
401 "rust".to_string(),
402 "fn test() {}".to_string(),
403 );
404
405 let result = RuleApplicationEngine::apply_rule(&rule, &context);
406 assert!(result.matched);
407 assert_eq!(result.action, Some("add_documentation".to_string()));
408 }
409
410 #[test]
411 fn test_apply_multiple_rules() {
412 let rule1 = Rule::new(
413 crate::models::RuleScope::Session,
414 "function".to_string(),
415 "add_documentation".to_string(),
416 crate::models::RuleSource::Learned,
417 );
418
419 let rule2 = Rule::new(
420 crate::models::RuleScope::Session,
421 "rust".to_string(),
422 "add_error_handling".to_string(),
423 crate::models::RuleSource::Learned,
424 );
425
426 let context = GenerationContext::new(
427 "function".to_string(),
428 "rust".to_string(),
429 "fn test() {}".to_string(),
430 );
431
432 let results = RuleApplicationEngine::apply_rules(&[rule1, rule2], &context);
433 assert_eq!(results.len(), 2);
434 assert!(results[0].matched);
435 assert!(results[1].matched);
436 }
437
438 #[test]
439 fn test_apply_rules_with_precedence() {
440 let mut rule1 = Rule::new(
441 crate::models::RuleScope::Session,
442 "function".to_string(),
443 "add_documentation".to_string(),
444 crate::models::RuleSource::Learned,
445 );
446 rule1.confidence = 0.7;
447
448 let mut rule2 = Rule::new(
449 crate::models::RuleScope::Session,
450 "function".to_string(),
451 "add_error_handling".to_string(),
452 crate::models::RuleSource::Learned,
453 );
454 rule2.confidence = 0.9;
455
456 let context = GenerationContext::new(
457 "function".to_string(),
458 "rust".to_string(),
459 "fn test() {}".to_string(),
460 );
461
462 let result = RuleApplicationEngine::apply_rules_with_precedence(&[rule1, rule2], &context);
463 assert!(result.is_some());
464 assert_eq!(
465 result.unwrap().action,
466 Some("add_error_handling".to_string())
467 );
468 }
469
470 #[test]
471 fn test_chain_rules() {
472 let rule1 = Rule::new(
473 crate::models::RuleScope::Session,
474 "function".to_string(),
475 "add_documentation".to_string(),
476 crate::models::RuleSource::Learned,
477 );
478
479 let rule2 = Rule::new(
480 crate::models::RuleScope::Session,
481 "function".to_string(),
482 "add_error_handling".to_string(),
483 crate::models::RuleSource::Learned,
484 );
485
486 let context = GenerationContext::new(
487 "function".to_string(),
488 "rust".to_string(),
489 "fn test() {}".to_string(),
490 );
491
492 let results = RuleApplicationEngine::chain_rules(&[rule1, rule2], &context);
493 assert!(results.is_ok());
494 let results = results.unwrap();
495 assert_eq!(results.len(), 2);
496 }
497
498 #[test]
499 fn test_compose_rules() {
500 let rule1 = Rule::new(
501 crate::models::RuleScope::Session,
502 "function".to_string(),
503 "add_documentation".to_string(),
504 crate::models::RuleSource::Learned,
505 );
506
507 let rule2 = Rule::new(
508 crate::models::RuleScope::Session,
509 "function".to_string(),
510 "add_error_handling".to_string(),
511 crate::models::RuleSource::Learned,
512 );
513
514 let context = GenerationContext::new(
515 "function".to_string(),
516 "rust".to_string(),
517 "fn test() {}".to_string(),
518 );
519
520 let result = RuleApplicationEngine::compose_rules(&[rule1, rule2], &context);
521 assert!(result.is_ok());
522 let composed = result.unwrap();
523 assert!(composed.is_some());
524 let composed_str = composed.unwrap();
525 assert!(composed_str.contains("add_documentation"));
526 assert!(composed_str.contains("add_error_handling"));
527 }
528
529 #[test]
530 fn test_validate_rule_application() {
531 let rule = Rule::new(
532 crate::models::RuleScope::Session,
533 "function".to_string(),
534 "add_documentation".to_string(),
535 crate::models::RuleSource::Learned,
536 );
537
538 let context = GenerationContext::new(
539 "function".to_string(),
540 "rust".to_string(),
541 "fn test() {}".to_string(),
542 );
543
544 let result = RuleApplicationEngine::validate_rule_application(&rule, &context);
545 assert!(result.is_ok());
546 }
547
548 #[test]
549 fn test_validate_rule_application_empty_pattern() {
550 let mut rule = Rule::new(
551 crate::models::RuleScope::Session,
552 "function".to_string(),
553 "add_documentation".to_string(),
554 crate::models::RuleSource::Learned,
555 );
556 rule.pattern = String::new();
557
558 let context = GenerationContext::new(
559 "function".to_string(),
560 "rust".to_string(),
561 "fn test() {}".to_string(),
562 );
563
564 let result = RuleApplicationEngine::validate_rule_application(&rule, &context);
565 assert!(result.is_err());
566 }
567
568 #[test]
569 fn test_get_matching_rules() {
570 let rule1 = Rule::new(
571 crate::models::RuleScope::Session,
572 "function".to_string(),
573 "add_documentation".to_string(),
574 crate::models::RuleSource::Learned,
575 );
576
577 let rule2 = Rule::new(
578 crate::models::RuleScope::Session,
579 "class".to_string(),
580 "add_error_handling".to_string(),
581 crate::models::RuleSource::Learned,
582 );
583
584 let context = GenerationContext::new(
585 "function".to_string(),
586 "rust".to_string(),
587 "fn test() {}".to_string(),
588 );
589
590 let matching = RuleApplicationEngine::get_matching_rules(&[rule1, rule2], &context);
591 assert_eq!(matching.len(), 1);
592 assert_eq!(matching[0].action, "add_documentation");
593 }
594
595 #[test]
596 fn test_get_matching_rules_sorted_by_confidence() {
597 let mut rule1 = Rule::new(
598 crate::models::RuleScope::Session,
599 "function".to_string(),
600 "add_documentation".to_string(),
601 crate::models::RuleSource::Learned,
602 );
603 rule1.confidence = 0.5;
604
605 let mut rule2 = Rule::new(
606 crate::models::RuleScope::Session,
607 "function".to_string(),
608 "add_error_handling".to_string(),
609 crate::models::RuleSource::Learned,
610 );
611 rule2.confidence = 0.9;
612
613 let context = GenerationContext::new(
614 "function".to_string(),
615 "rust".to_string(),
616 "fn test() {}".to_string(),
617 );
618
619 let matching = RuleApplicationEngine::get_matching_rules_sorted(&[rule1, rule2], &context);
620 assert_eq!(matching.len(), 2);
621 assert_eq!(matching[0].confidence, 0.9);
622 assert_eq!(matching[1].confidence, 0.5);
623 }
624}