tailwind_rs_postcss/css_optimizer/
rule_merger.rs

1//! Rule merger for consolidating compatible CSS rules
2
3use regex::Regex;
4use std::collections::HashMap;
5use super::types::*;
6
7/// Rule merger for consolidating compatible CSS rules
8pub struct RuleMerger {
9    compatibility_matrix: HashMap<String, Vec<String>>,
10}
11
12impl RuleMerger {
13    /// Create new rule merger
14    pub fn new() -> Self {
15        Self {
16            compatibility_matrix: Self::build_compatibility_matrix(),
17        }
18    }
19    
20    /// Merge compatible CSS rules
21    pub fn merge_rules(&self, css: &str) -> Result<RuleMergeResult, OptimizationError> {
22        let start_time = std::time::Instant::now();
23        let mut rules_merged = 0;
24        let mut optimized_css = css.to_string();
25        
26        // Find and merge duplicate selectors
27        let duplicate_selectors = self.find_duplicate_selectors(css)?;
28        
29        for (selector, rules) in duplicate_selectors {
30            if rules.len() > 1 {
31                let merged_rule = self.merge_rule_properties(&rules)?;
32                optimized_css = self.replace_duplicate_rules(&optimized_css, &selector, &merged_rule)?;
33                rules_merged += 1;
34            }
35        }
36        
37        Ok(RuleMergeResult {
38            optimized_css,
39            rules_merged,
40            merge_time: start_time.elapsed(),
41        })
42    }
43    
44    /// Find duplicate selectors in CSS
45    fn find_duplicate_selectors(&self, css: &str) -> Result<HashMap<String, Vec<CSSRule>>, OptimizationError> {
46        let mut selector_map: HashMap<String, Vec<CSSRule>> = HashMap::new();
47        let rule_pattern = Regex::new(r"([^{]+)\s*\{([^}]+)\}").unwrap();
48        
49        for cap in rule_pattern.captures_iter(css) {
50            let selector = cap[1].trim().to_string();
51            let properties = cap[2].trim();
52            
53            let rule = CSSRule {
54                selector: selector.clone(),
55                properties: self.parse_properties(properties)?,
56            };
57            
58            selector_map.entry(selector).or_insert_with(Vec::new).push(rule);
59        }
60        
61        Ok(selector_map)
62    }
63    
64    /// Merge properties from multiple rules
65    fn merge_rule_properties(&self, rules: &[CSSRule]) -> Result<CSSRule, OptimizationError> {
66        let mut merged_properties = Vec::new();
67        let mut seen_properties: HashMap<String, String> = HashMap::new();
68        
69        for rule in rules {
70            for property in &rule.properties {
71                seen_properties.insert(property.name.clone(), property.value.clone());
72            }
73        }
74        
75        for (name, value) in seen_properties {
76            merged_properties.push(CSSProperty { name, value });
77        }
78        
79        Ok(CSSRule {
80            selector: rules[0].selector.clone(),
81            properties: merged_properties,
82        })
83    }
84    
85    /// Replace duplicate rules with merged rule
86    fn replace_duplicate_rules(&self, css: &str, selector: &str, merged_rule: &CSSRule) -> Result<String, OptimizationError> {
87        let mut result = css.to_string();
88        let rule_pattern = Regex::new(&format!(r"{}[^{{]*\{{[^}}]*\}}", regex::escape(selector))).unwrap();
89        
90        // Remove all instances of the selector
91        result = rule_pattern.replace_all(&result, "").to_string();
92        
93        // Add merged rule
94        let merged_css = format!("{} {{\n{}\n}}", merged_rule.selector, 
95            merged_rule.properties.iter()
96                .map(|p| format!("  {}: {};", p.name, p.value))
97                .collect::<Vec<_>>()
98                .join("\n"));
99        
100        result.push_str(&merged_css);
101        Ok(result)
102    }
103    
104    /// Parse CSS properties from string
105    fn parse_properties(&self, properties_str: &str) -> Result<Vec<CSSProperty>, OptimizationError> {
106        let mut properties = Vec::new();
107        let property_pattern = Regex::new(r"([^:]+):\s*([^;]+);").unwrap();
108        
109        for cap in property_pattern.captures_iter(properties_str) {
110            properties.push(CSSProperty {
111                name: cap[1].trim().to_string(),
112                value: cap[2].trim().to_string(),
113            });
114        }
115        
116        Ok(properties)
117    }
118    
119    /// Build compatibility matrix for different selector types
120    fn build_compatibility_matrix() -> HashMap<String, Vec<String>> {
121        let mut matrix = HashMap::new();
122        
123        // Class selectors can be merged with other classes
124        matrix.insert("class".to_string(), vec![
125            "class".to_string(),
126            "element".to_string(),
127        ]);
128        
129        // Element selectors can be merged with classes
130        matrix.insert("element".to_string(), vec![
131            "element".to_string(),
132            "class".to_string(),
133        ]);
134        
135        matrix
136    }
137}