tailwind_rs_postcss/css_optimizer/
property_optimizer.rs

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