tailwind_rs_postcss/css_optimizer/
property_optimizer.rs1use regex::Regex;
4use std::collections::HashMap;
5use super::types::*;
6
7pub struct PropertyOptimizer {
9 property_rules: HashMap<String, PropertyRule>,
10}
11
12impl PropertyOptimizer {
13 pub fn new() -> Self {
15 Self {
16 property_rules: Self::build_property_rules(),
17 }
18 }
19
20 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 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 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 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 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 fn count_properties(&self, properties_str: &str) -> usize {
86 properties_str.matches(';').count()
87 }
88
89 fn build_property_rules() -> HashMap<String, PropertyRule> {
91 let mut rules = HashMap::new();
92
93 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}