driven/templates/
composer.rs1use super::TemplateRegistry;
4use crate::{Result, parser::UnifiedRule};
5
6#[derive(Debug)]
8pub struct TemplateComposer<'a> {
9 registry: &'a TemplateRegistry,
10}
11
12impl<'a> TemplateComposer<'a> {
13 pub fn new(registry: &'a TemplateRegistry) -> Self {
15 Self { registry }
16 }
17
18 pub fn compose(&self, template_names: &[&str]) -> Result<Vec<UnifiedRule>> {
20 let mut rules = Vec::new();
21
22 for name in template_names {
23 if let Some(template) = self.registry.get(name) {
24 rules.extend(template.expand()?);
25 }
26 }
27
28 self.deduplicate(&mut rules);
30
31 Ok(rules)
32 }
33
34 pub fn compose_with_priority(&self, template_names: &[&str]) -> Result<Vec<UnifiedRule>> {
36 let mut all_rules: Vec<(usize, UnifiedRule)> = Vec::new();
37
38 for (priority, name) in template_names.iter().enumerate() {
40 if let Some(template) = self.registry.get(name) {
41 for rule in template.expand()? {
42 all_rules.push((priority, rule));
43 }
44 }
45 }
46
47 all_rules.sort_by_key(|(p, _)| std::cmp::Reverse(*p));
49
50 let rules: Vec<UnifiedRule> = all_rules.into_iter().map(|(_, r)| r).collect();
51 Ok(rules)
52 }
53
54 fn deduplicate(&self, rules: &mut Vec<UnifiedRule>) {
56 let mut seen_descriptions = std::collections::HashSet::new();
57 rules.retain(|rule| {
58 if let UnifiedRule::Standard { description, .. } = rule {
59 seen_descriptions.insert(description.clone())
60 } else {
61 true
62 }
63 });
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn test_compose_empty() {
73 let registry = TemplateRegistry::new();
74 let composer = TemplateComposer::new(®istry);
75
76 let rules = composer.compose(&[]).unwrap();
77 assert!(rules.is_empty());
78 }
79
80 #[test]
81 fn test_compose_single() {
82 let registry = TemplateRegistry::new();
83 let composer = TemplateComposer::new(®istry);
84
85 let rules = composer.compose(&["architect"]).unwrap();
86 assert!(!rules.is_empty());
87 }
88
89 #[test]
90 fn test_compose_multiple() {
91 let registry = TemplateRegistry::new();
92 let composer = TemplateComposer::new(®istry);
93
94 let rules = composer
95 .compose(&["architect", "rust-idioms", "testing"])
96 .unwrap();
97 assert!(!rules.is_empty());
98 }
99}