tailwind_rs_postcss/css_optimizer/
property_optimizer.rs1use super::types::*;
4use regex::Regex;
5use std::collections::HashMap;
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(
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 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 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 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 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 fn count_properties(&self, properties_str: &str) -> usize {
96 properties_str.matches(';').count()
97 }
98
99 fn build_property_rules() -> HashMap<String, PropertyRule> {
101 let mut rules = HashMap::new();
102
103 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}