tailwind_rs_postcss/purger/
class_extractor.rs

1//! Class extractor for CSS classes
2
3use super::types::*;
4use regex::Regex;
5use std::collections::HashSet;
6
7/// Class extractor for CSS classes
8pub struct ClassExtractor {
9    _class_patterns: Vec<String>,
10    css_rule_pattern: Regex,
11}
12
13impl ClassExtractor {
14    /// Create new class extractor
15    pub fn new() -> Self {
16        Self {
17            _class_patterns: Self::get_default_patterns(),
18            css_rule_pattern: Regex::new(r"([^{]+)\s*\{([^}]+)\}").unwrap(),
19        }
20    }
21
22    /// Extract classes from CSS
23    pub fn extract_classes(&self, css: &str) -> Result<HashSet<String>, PurgeError> {
24        let mut classes = HashSet::new();
25
26        // Extract classes from CSS selectors
27        for cap in self.css_rule_pattern.captures_iter(css) {
28            let selector = cap[1].trim();
29            let selector_classes = self.extract_classes_from_selector(selector);
30            classes.extend(selector_classes);
31        }
32
33        Ok(classes)
34    }
35
36    /// Extract classes from a CSS selector
37    fn extract_classes_from_selector(&self, selector: &str) -> HashSet<String> {
38        let mut classes = HashSet::new();
39
40        // Split selector by combinators
41        let parts: Vec<&str> = selector.split(&[' ', '>', '+', '~', ','][..]).collect();
42
43        for part in parts {
44            let part = part.trim();
45            if part.starts_with('.') {
46                // Extract class name
47                let class_name = part.strip_prefix('.').unwrap_or(part);
48                classes.insert(class_name.to_string());
49            }
50        }
51
52        classes
53    }
54
55    /// Get default class patterns
56    fn get_default_patterns() -> Vec<String> {
57        vec![
58            r"\.([a-zA-Z0-9_-]+)".to_string(),
59            "class\\s*=\\s*[\"']([^\"']+)[\"']".to_string(),
60        ]
61    }
62}