tailwind_rs_postcss/css_optimizer/
selector_optimizer.rs

1//! Selector optimizer for simplification and optimization
2
3use regex::Regex;
4use std::collections::HashMap;
5use super::types::*;
6
7/// Selector optimizer for simplification and optimization
8pub struct SelectorOptimizer {
9    selector_rules: HashMap<String, SelectorRule>,
10}
11
12impl SelectorOptimizer {
13    /// Create new selector optimizer
14    pub fn new() -> Self {
15        Self {
16            selector_rules: Self::build_selector_rules(),
17        }
18    }
19    
20    /// Optimize CSS selectors
21    pub fn optimize_selectors(&self, css: &str) -> Result<SelectorOptimizationResult, OptimizationError> {
22        let start_time = std::time::Instant::now();
23        let mut selectors_optimized = 0;
24        let mut optimized_css = css.to_string();
25        
26        // Find and optimize redundant selectors
27        let redundant_selectors = self.find_redundant_selectors(css)?;
28        
29        for selector in redundant_selectors {
30            optimized_css = self.remove_redundant_selector(&optimized_css, &selector)?;
31            selectors_optimized += 1;
32        }
33        
34        Ok(SelectorOptimizationResult {
35            optimized_css,
36            selectors_optimized,
37            optimization_time: start_time.elapsed(),
38        })
39    }
40    
41    /// Find redundant selectors
42    fn find_redundant_selectors(&self, css: &str) -> Result<Vec<String>, OptimizationError> {
43        let mut redundant = Vec::new();
44        let rule_pattern = Regex::new(r"([^{]+)\s*\{([^}]+)\}").unwrap();
45        let mut selector_map: HashMap<String, Vec<String>> = HashMap::new();
46        
47        for cap in rule_pattern.captures_iter(css) {
48            let selector = cap[1].trim().to_string();
49            let properties = cap[2].trim().to_string();
50            
51            selector_map.entry(properties).or_insert_with(Vec::new).push(selector);
52        }
53        
54        // Find selectors with identical properties
55        for (_, selectors) in selector_map {
56            if selectors.len() > 1 {
57                // Keep the most specific selector, mark others as redundant
58                let mut sorted_selectors = selectors;
59                sorted_selectors.sort_by(|a, b| self.compare_specificity(a, b));
60                
61                for selector in sorted_selectors.iter().skip(1) {
62                    redundant.push(selector.clone());
63                }
64            }
65        }
66        
67        Ok(redundant)
68    }
69    
70    /// Remove redundant selector from CSS
71    fn remove_redundant_selector(&self, css: &str, selector: &str) -> Result<String, OptimizationError> {
72        let rule_pattern = Regex::new(&format!(r"{}[^{{]*\{{[^}}]*\}}", regex::escape(selector))).unwrap();
73        Ok(rule_pattern.replace_all(css, "").to_string())
74    }
75    
76    /// Compare selector specificity
77    fn compare_specificity(&self, a: &str, b: &str) -> std::cmp::Ordering {
78        let specificity_a = self.calculate_specificity(a);
79        let specificity_b = self.calculate_specificity(b);
80        specificity_b.cmp(&specificity_a) // Higher specificity first
81    }
82    
83    /// Calculate selector specificity
84    fn calculate_specificity(&self, selector: &str) -> usize {
85        let mut specificity = 0;
86        
87        // Count IDs
88        specificity += selector.matches('#').count() * 100;
89        
90        // Count classes and attributes
91        specificity += selector.matches('.').count() * 10;
92        specificity += selector.matches('[').count() * 10;
93        
94        // Count elements
95        specificity += selector.split_whitespace().count();
96        
97        specificity
98    }
99    
100    /// Build selector rules for optimization
101    fn build_selector_rules() -> HashMap<String, SelectorRule> {
102        let mut rules = HashMap::new();
103        
104        rules.insert("universal".to_string(), SelectorRule {
105            name: "universal".to_string(),
106            pattern: "*".to_string(),
107            optimization: SelectorOptimization::Remove,
108        });
109        
110        rules
111    }
112}