tailwind_rs_postcss/css_optimizer/
property_optimizer.rs

1//! Property optimizer for deduplication and optimization
2
3use regex::Regex;
4use std::collections::HashMap;
5use super::types::*;
6
7/// Property optimizer for deduplication and optimization
8pub struct PropertyOptimizer {
9    property_rules: HashMap<String, PropertyRule>,
10}
11
12impl PropertyOptimizer {
13    /// Create new property optimizer
14    pub fn new() -> Self {
15        Self {
16            property_rules: Self::build_property_rules(),
17        }
18    }
19    
20    /// Optimize CSS properties
21    pub fn optimize_properties(&self, css: &str) -> Result<PropertyOptimizationResult, OptimizationError> {
22        let start_time = std::time::Instant::now();
23        let mut properties_optimized = 0;
24        let mut duplicates_removed = 0;
25        let mut optimized_css = css.to_string();
26        
27        // Remove duplicate properties within rules
28        let rule_pattern = Regex::new(r"([^{]+)\s*\{([^}]+)\}").unwrap();
29        
30        for cap in rule_pattern.captures_iter(css) {
31            let selector = cap[1].trim();
32            let properties_str = cap[2].trim();
33            
34            let optimized_properties = self.optimize_rule_properties(properties_str)?;
35            let original_count = self.count_properties(properties_str);
36            let optimized_count = optimized_properties.len();
37            
38            if optimized_count < original_count {
39                duplicates_removed += original_count - optimized_count;
40                properties_optimized += optimized_count;
41                
42                // Replace the rule with optimized version
43                let optimized_rule = format!("{} {{\n{}\n}}", selector,
44                    optimized_properties.iter()
45                        .map(|p| format!("  {}: {};", p.name, p.value))
46                        .collect::<Vec<_>>()
47                        .join("\n"));
48                
49                let old_rule = &cap[0];
50                optimized_css = optimized_css.replace(old_rule, &optimized_rule);
51            }
52        }
53        
54        Ok(PropertyOptimizationResult {
55            optimized_css,
56            properties_optimized,
57            duplicates_removed,
58            optimization_time: start_time.elapsed(),
59        })
60    }
61    
62    /// Optimize properties within a single rule
63    fn optimize_rule_properties(&self, properties_str: &str) -> Result<Vec<CSSProperty>, OptimizationError> {
64        let mut properties = Vec::new();
65        let mut seen_properties: HashMap<String, String> = HashMap::new();
66        
67        let property_pattern = Regex::new(r"([^:]+):\s*([^;]+);").unwrap();
68        
69        for cap in property_pattern.captures_iter(properties_str) {
70            let name = cap[1].trim().to_string();
71            let value = cap[2].trim().to_string();
72            
73            // Keep the last value for duplicate properties (CSS cascade rule)
74            seen_properties.insert(name, value);
75        }
76        
77        for (name, value) in seen_properties {
78            properties.push(CSSProperty { name, value });
79        }
80        
81        Ok(properties)
82    }
83    
84    /// Count properties in a string
85    fn count_properties(&self, properties_str: &str) -> usize {
86        properties_str.matches(';').count()
87    }
88    
89    /// Build property rules for optimization
90    fn build_property_rules() -> HashMap<String, PropertyRule> {
91        let mut rules = HashMap::new();
92        
93        // Add rules for common property optimizations
94        rules.insert("margin".to_string(), PropertyRule {
95            name: "margin".to_string(),
96            shorthand: true,
97            longhand: vec!["margin-top".to_string(), "margin-right".to_string(), 
98                          "margin-bottom".to_string(), "margin-left".to_string()],
99        });
100        
101        rules.insert("padding".to_string(), PropertyRule {
102            name: "padding".to_string(),
103            shorthand: true,
104            longhand: vec!["padding-top".to_string(), "padding-right".to_string(), 
105                          "padding-bottom".to_string(), "padding-left".to_string()],
106        });
107        
108        rules
109    }
110}