@@ -7,64 +7,96 @@ use crate::{
77} ;
88
99pub fn optimize_value ( value : & str ) -> String {
10- let mut ret = value. trim ( ) . to_string ( ) ;
10+ let trimmed = value. trim ( ) ;
11+ let mut ret = String :: with_capacity ( trimmed. len ( ) + 8 ) ;
12+ ret. push_str ( trimmed) ;
1113
1214 // Wrap CSS custom property names in var() when used as values
1315 // e.g., "--var-0" becomes "var(--var-0)"
1416 if ret. starts_with ( "--" ) && !ret. contains ( ' ' ) && !ret. contains ( ',' ) {
15- ret = format ! ( "var({})" , ret) ;
17+ ret. insert_str ( 0 , "var(" ) ;
18+ ret. push ( ')' ) ;
1619 }
1720
18- ret = INNER_TRIM_RE . replace_all ( & ret, "(${1})" ) . to_string ( ) ;
21+ // Use Cow-aware replacement: only allocate when regex matches
22+ let replaced = INNER_TRIM_RE . replace_all ( & ret, "(${1})" ) ;
23+ if let std:: borrow:: Cow :: Owned ( s) = replaced {
24+ ret = s;
25+ }
1926
2027 // Skip RM_MINUS_ZERO_RE for values containing CSS custom property references
2128 // to preserve names like --var-0 (the -0 should not be converted to 0)
2229 if !ret. contains ( "--" ) {
23- ret = RM_MINUS_ZERO_RE . replace_all ( & ret, "0${1}" ) . to_string ( ) ;
30+ let replaced = RM_MINUS_ZERO_RE . replace_all ( & ret, "0${1}" ) ;
31+ if let std:: borrow:: Cow :: Owned ( s) = replaced {
32+ ret = s;
33+ }
34+ }
35+ let replaced = NUM_TRIM_RE . replace_all ( & ret, "${1} ${3}" ) ;
36+ if let std:: borrow:: Cow :: Owned ( s) = replaced {
37+ ret = s;
2438 }
25- ret = NUM_TRIM_RE . replace_all ( & ret, "${1} ${3}" ) . to_string ( ) ;
2639
27- if ret. contains ( "," ) {
28- ret = F_SPACE_RE . replace_all ( & ret, "," ) . trim ( ) . to_string ( ) ;
40+ if ret. contains ( ',' ) {
41+ let replaced = F_SPACE_RE . replace_all ( & ret, "," ) ;
42+ if let std:: borrow:: Cow :: Owned ( s) = replaced {
43+ ret = s;
44+ }
45+ let trimmed = ret. trim ( ) ;
46+ if trimmed. len ( ) != ret. len ( ) {
47+ ret = trimmed. to_string ( ) ;
48+ }
49+ }
50+ let replaced = F_RGBA_RE . replace_all ( & ret, |c : & regex:: Captures | {
51+ let r = c[ 1 ] . parse :: < i32 > ( ) . unwrap ( ) ;
52+ let g = c[ 2 ] . parse :: < i32 > ( ) . unwrap ( ) ;
53+ let b = c[ 3 ] . parse :: < i32 > ( ) . unwrap ( ) ;
54+ let a = c[ 4 ] . parse :: < f32 > ( ) . unwrap ( ) ;
55+ format ! (
56+ "#{:02X}{:02X}{:02X}{:02X}" ,
57+ r,
58+ g,
59+ b,
60+ ( a * 255.0 ) . round( ) as i32
61+ )
62+ } ) ;
63+ if let std:: borrow:: Cow :: Owned ( s) = replaced {
64+ ret = s;
2965 }
30- ret = F_RGBA_RE
31- . replace_all ( & ret, |c : & regex:: Captures | {
32- let r = c[ 1 ] . parse :: < i32 > ( ) . unwrap ( ) ;
33- let g = c[ 2 ] . parse :: < i32 > ( ) . unwrap ( ) ;
34- let b = c[ 3 ] . parse :: < i32 > ( ) . unwrap ( ) ;
35- let a = c[ 4 ] . parse :: < f32 > ( ) . unwrap ( ) ;
36- format ! (
37- "#{:02X}{:02X}{:02X}{:02X}" ,
38- r,
39- g,
40- b,
41- ( a * 255.0 ) . round( ) as i32
42- )
43- } )
44- . to_string ( ) ;
45- ret = F_RGB_RE
46- . replace_all ( & ret, |c : & regex:: Captures | {
47- let r = c[ 1 ] . parse :: < i32 > ( ) . unwrap ( ) ;
48- let g = c[ 2 ] . parse :: < i32 > ( ) . unwrap ( ) ;
49- let b = c[ 3 ] . parse :: < i32 > ( ) . unwrap ( ) ;
50- format ! ( "#{r:02X}{g:02X}{b:02X}" )
51- } )
52- . to_string ( ) ;
53- if ret. contains ( "#" ) {
54- ret = COLOR_HASH
55- . replace_all ( & ret, |c : & regex:: Captures | optimize_color ( & c[ 1 ] ) )
56- . to_string ( ) ;
66+ let replaced = F_RGB_RE . replace_all ( & ret, |c : & regex:: Captures | {
67+ let r = c[ 1 ] . parse :: < i32 > ( ) . unwrap ( ) ;
68+ let g = c[ 2 ] . parse :: < i32 > ( ) . unwrap ( ) ;
69+ let b = c[ 3 ] . parse :: < i32 > ( ) . unwrap ( ) ;
70+ format ! ( "#{r:02X}{g:02X}{b:02X}" )
71+ } ) ;
72+ if let std:: borrow:: Cow :: Owned ( s) = replaced {
73+ ret = s;
5774 }
58- if ret. contains ( "0" ) {
59- ret = DOT_ZERO_RE . replace_all ( & ret, "${1}0${2}" ) . to_string ( ) ;
60- ret = F_DOT_RE . replace_all ( & ret, "${1}.${2}" ) . to_string ( ) ;
61- ret = ZERO_RE . replace_all ( & ret, "${1}0" ) . to_string ( ) ;
75+ if ret. contains ( '#' ) {
76+ let replaced = COLOR_HASH . replace_all ( & ret, |c : & regex:: Captures | optimize_color ( & c[ 1 ] ) ) ;
77+ if let std:: borrow:: Cow :: Owned ( s) = replaced {
78+ ret = s;
79+ }
80+ }
81+ if ret. contains ( '0' ) {
82+ let replaced = DOT_ZERO_RE . replace_all ( & ret, "${1}0${2}" ) ;
83+ if let std:: borrow:: Cow :: Owned ( s) = replaced {
84+ ret = s;
85+ }
86+ let replaced = F_DOT_RE . replace_all ( & ret, "${1}.${2}" ) ;
87+ if let std:: borrow:: Cow :: Owned ( s) = replaced {
88+ ret = s;
89+ }
90+ let replaced = ZERO_RE . replace_all ( & ret, "${1}0" ) ;
91+ if let std:: borrow:: Cow :: Owned ( s) = replaced {
92+ ret = s;
93+ }
6294
6395 for f in ZERO_PERCENT_FUNCTION . iter ( ) {
6496 let tmp = ret. to_lowercase ( ) ;
6597 if tmp. contains ( f) {
6698 let index = tmp. find ( f) . unwrap ( ) + f. len ( ) ;
67- let mut zero_idx = vec ! [ ] ;
99+ let mut zero_idx = Vec :: with_capacity ( 4 ) ;
68100 let mut depth = 0 ;
69101 let chars: Vec < char > = tmp. chars ( ) . collect ( ) ;
70102 let byte_indices: Vec < usize > = tmp. char_indices ( ) . map ( |( i, _) | i) . collect ( ) ;
@@ -82,31 +114,51 @@ pub fn optimize_value(value: &str) -> String {
82114 zero_idx. push ( byte_indices[ char_idx] ) ;
83115 }
84116 }
117+ // In-place replacement: replace each '0' with '0%' from back to front
85118 for i in zero_idx. iter ( ) . rev ( ) {
86- ret = ret [ .. * i ] . to_string ( ) + "0%" + & ret [ * i + 1 .. ] ;
119+ ret. replace_range ( * i.. * i + 1 , "0%" ) ;
87120 }
88121 }
89122 }
90123 }
91124 // remove ; from dynamic value
125+ // Check suffix patterns directly without format! allocation
92126 for str_symbol in [ "" , "`" , "\" " , "'" ] {
93- if ret. ends_with ( & format ! ( ";{str_symbol}" ) ) {
94- ret = format ! (
95- "{}{}" ,
96- ret[ ..ret. len( ) - str_symbol. len( ) - 1 ] . trim_end_matches( ';' ) ,
97- str_symbol
98- ) ;
99- } else if ret. ends_with ( & format ! ( ";{str_symbol})" ) ) {
100- ret = format ! (
101- "{}{})" ,
102- ret[ ..ret. len( ) - str_symbol. len( ) - 2 ] . trim_end_matches( ';' ) ,
103- str_symbol
104- ) ;
127+ let suffix_with_paren = if str_symbol. is_empty ( ) {
128+ ";)" . to_string ( )
129+ } else {
130+ let mut s = String :: with_capacity ( str_symbol. len ( ) + 2 ) ;
131+ s. push ( ';' ) ;
132+ s. push_str ( str_symbol) ;
133+ s. push ( ')' ) ;
134+ s
135+ } ;
136+ let suffix_without_paren = if str_symbol. is_empty ( ) {
137+ ";" . to_string ( )
138+ } else {
139+ let mut s = String :: with_capacity ( str_symbol. len ( ) + 1 ) ;
140+ s. push ( ';' ) ;
141+ s. push_str ( str_symbol) ;
142+ s
143+ } ;
144+ if ret. ends_with ( & suffix_without_paren) {
145+ let base = ret[ ..ret. len ( ) - suffix_without_paren. len ( ) ] . trim_end_matches ( ';' ) ;
146+ let mut new_ret = String :: with_capacity ( base. len ( ) + str_symbol. len ( ) ) ;
147+ new_ret. push_str ( base) ;
148+ new_ret. push_str ( str_symbol) ;
149+ ret = new_ret;
150+ } else if ret. ends_with ( & suffix_with_paren) {
151+ let base = ret[ ..ret. len ( ) - suffix_with_paren. len ( ) ] . trim_end_matches ( ';' ) ;
152+ let mut new_ret = String :: with_capacity ( base. len ( ) + str_symbol. len ( ) + 1 ) ;
153+ new_ret. push_str ( base) ;
154+ new_ret. push_str ( str_symbol) ;
155+ new_ret. push ( ')' ) ;
156+ ret = new_ret;
105157 }
106158 }
107159
108- if ret. contains ( "(" ) || ret. contains ( ")" ) {
109- let mut depth = 0 ;
160+ if ret. contains ( '(' ) || ret. contains ( ')' ) {
161+ let mut depth: i32 = 0 ;
110162 for ch in ret. chars ( ) {
111163 if ch == '(' {
112164 depth += 1 ;
0 commit comments