@@ -10,6 +10,21 @@ use crate::analyzers::Analyzer;
1010use crate :: fleet:: { Finding , ImpactAssessment , Severity , WcagLevel } ;
1111use regex:: Regex ;
1212use std:: path:: Path ;
13+ use std:: sync:: LazyLock ;
14+
15+ // Compiled once. These were rebuilt on every call (hypatia expect_in_hot_path).
16+ static COLOR_RE : LazyLock < Regex > =
17+ LazyLock :: new ( || Regex :: new ( r"(?i)(?:^|;|\{)\s*color\s*:\s*([^;}\n]+)" ) . expect ( "valid regex" ) ) ;
18+ static BG_RE : LazyLock < Regex > =
19+ LazyLock :: new ( || Regex :: new ( r"(?i)background(?:-color)?\s*:\s*([^;}\n]+)" ) . expect ( "valid regex" ) ) ;
20+ static BLOCK_RE : LazyLock < Regex > =
21+ LazyLock :: new ( || Regex :: new ( r"([^{]+)\{([^}]+)\}" ) . expect ( "valid regex" ) ) ;
22+ static STYLE_RE : LazyLock < Regex > =
23+ LazyLock :: new ( || Regex :: new ( r#"style\s*=\s*"([^"]+)""# ) . expect ( "valid regex" ) ) ;
24+ static COLOR_RE_4 : LazyLock < Regex > =
25+ LazyLock :: new ( || Regex :: new ( r"(?i)(?:^|;)\s*color\s*:\s*([^;]+)" ) . expect ( "valid regex" ) ) ;
26+ static BG_RE_5 : LazyLock < Regex > =
27+ LazyLock :: new ( || Regex :: new ( r"(?i)background(?:-color)?\s*:\s*([^;]+)" ) . expect ( "valid regex" ) ) ;
1328
1429/// Contrast analyzer for CSS color pairs
1530pub struct ContrastAnalyzer ;
@@ -133,15 +148,11 @@ pub fn contrast_ratio(fg: (u8, u8, u8), bg: (u8, u8, u8)) -> f64 {
133148/// Analyze CSS content for contrast issues
134149fn analyze_css ( path : & Path , content : & str ) -> Vec < Finding > {
135150 let mut findings = Vec :: new ( ) ;
136- let color_re = Regex :: new (
137- r"(?i)(?:^|;|\{)\s*color\s*:\s*([^;}\n]+)"
138- ) . expect ( "valid regex" ) ;
139- let bg_re = Regex :: new (
140- r"(?i)background(?:-color)?\s*:\s*([^;}\n]+)"
141- ) . expect ( "valid regex" ) ;
151+ let color_re = & * COLOR_RE ;
152+ let bg_re = & * BG_RE ;
142153
143154 // Extract color/background-color pairs within CSS rule blocks
144- let block_re = Regex :: new ( r"([^{]+)\{([^}]+)\}" ) . expect ( "valid regex" ) ;
155+ let block_re = & * BLOCK_RE ;
145156
146157 for caps in block_re. captures_iter ( content) {
147158 let selector = caps[ 1 ] . trim ( ) ;
@@ -211,9 +222,9 @@ fn analyze_css(path: &Path, content: &str) -> Vec<Finding> {
211222/// Analyze inline styles in HTML for contrast issues
212223fn analyze_inline_styles ( path : & Path , content : & str ) -> Vec < Finding > {
213224 let mut findings = Vec :: new ( ) ;
214- let style_re = Regex :: new ( r#"style\s*=\s*"([^"]+)""# ) . expect ( "valid regex" ) ;
215- let color_re = Regex :: new ( r"(?i)(?:^|;)\s*color\s*:\s*([^;]+)" ) . expect ( "valid regex" ) ;
216- let bg_re = Regex :: new ( r"(?i)background(?:-color)?\s*:\s*([^;]+)" ) . expect ( "valid regex" ) ;
225+ let style_re = & * STYLE_RE ;
226+ let color_re = & * COLOR_RE_4 ;
227+ let bg_re = & * BG_RE_5 ;
217228
218229 for ( line_num, line) in content. lines ( ) . enumerate ( ) {
219230 if let Some ( style_caps) = style_re. captures ( line) {
0 commit comments