@@ -34,34 +34,105 @@ macro_rules! match_ignore_ascii_case {
3434 ( $input: expr,
3535 $(
3636 $( #[ $meta: meta] ) *
37- $( $pattern: pat ) |+ $( if $guard: expr ) ? => $then: expr
37+ $( $pattern: literal ) |+ $( if $guard: expr ) ? => $then: expr
3838 ) ,+
39+ $( , _ => $fallback: expr) ?
3940 $( , ) ?
4041 ) => {
4142 {
42- // This dummy module works around the feature gate
43- // `error[E0658]: procedural macros cannot be expanded to statements`
44- // by forcing the macro to be in an item context
45- // rather than expression/statement context,
46- // even though the macro only expands to items.
47- mod cssparser_internal {
48- $crate:: _cssparser_internal_max_len! {
49- $( $( $pattern ) + ) +
43+ #[ inline( always) ]
44+ const fn const_usize_max( a: usize , b: usize ) -> usize {
45+ if a > b {
46+ a
47+ } else {
48+ b
5049 }
5150 }
52- $crate:: _cssparser_internal_to_lowercase!( $input, cssparser_internal:: MAX_LENGTH => lowercase) ;
51+
52+ const MAX_LENGTH : usize = {
53+ let mut maxlen : usize = 0 ;
54+ $(
55+ $( #[ $meta] ) *
56+ // {} is necessary to work around "[E0658]: attributes on expressions are experimental"
57+ {
58+ $( maxlen = const_usize_max( maxlen, $pattern. len( ) ) ; ) +
59+ }
60+ ) +
61+ maxlen
62+ } ;
63+
64+ $crate:: _cssparser_internal_to_lowercase!( $input, MAX_LENGTH => lowercase) ;
5365 // "A" is a short string that we know is different for every string pattern,
5466 // since we’ve verified that none of them include ASCII upper case letters.
5567 match lowercase. unwrap_or( "A" ) {
5668 $(
5769 $( #[ $meta] ) *
5870 $( $pattern ) |+ $( if $guard ) ? => $then,
5971 ) +
72+ $( _ => $fallback, ) ?
6073 }
6174 }
6275 } ;
6376}
6477
78+ #[ cfg( not( feature = "fast_match_color" ) ) ]
79+ #[ macro_export]
80+ /// Define a function `$name(&str) -> Option<&'static $ValueType>`
81+ ///
82+ /// The function finds a match for the input string
83+ /// in a [`phf` map](https://github.com/sfackler/rust-phf)
84+ /// and returns a reference to the corresponding value.
85+ /// Matching is case-insensitive in the ASCII range.
86+ ///
87+ /// ## Example:
88+ ///
89+ /// ```rust
90+ /// # fn main() {} // Make doctest not wrap everything in its own main
91+ ///
92+ /// fn color_rgb(input: &str) -> Option<(u8, u8, u8)> {
93+ /// cssparser::ascii_case_insensitive_map! {
94+ /// keywords -> (u8, u8, u8) = {
95+ /// "red" => (255, 0, 0),
96+ /// "green" => (0, 255, 0),
97+ /// "blue" => (0, 0, 255),
98+ /// }
99+ /// }
100+ /// keywords::get(input).cloned()
101+ /// }
102+ /// ```
103+ ///
104+ /// You can also iterate over the map entries by using `keywords::entries()`.
105+ macro_rules! ascii_case_insensitive_map {
106+ ( $name: ident -> $ValueType: ty = { $( $key: tt => $value: expr ) ,+ } ) => {
107+ ascii_case_insensitive_map!( $name -> $ValueType = { $( $key => $value, ) + } )
108+ } ;
109+ ( $name: ident -> $ValueType: ty = { $( $key: tt => $value: expr, ) + } ) => {
110+
111+ // While the obvious choice for this would be an inner module, it's not possible to
112+ // reference from types from there, see:
113+ // <https://github.com/rust-lang/rust/issues/114369>
114+ //
115+ // So we abuse a struct with static associated functions instead.
116+ #[ allow( non_camel_case_types) ]
117+ struct $name;
118+ impl $name {
119+ #[ allow( dead_code) ]
120+ fn entries( ) -> impl Iterator <Item = ( & ' static & ' static str , & ' static $ValueType) > {
121+ [ $( ( & $key, & $value) , ) * ] . iter( ) . copied( )
122+ }
123+
124+ fn get( input: & str ) -> Option <& ' static $ValueType> {
125+ $crate:: match_ignore_ascii_case!( input,
126+ $( $key => Some ( & $value) , ) *
127+ _ => None ,
128+ )
129+ }
130+ }
131+ }
132+ }
133+
134+ #[ cfg( feature = "fast_match_color" ) ]
135+ #[ macro_export]
65136/// Define a function `$name(&str) -> Option<&'static $ValueType>`
66137///
67138/// The function finds a match for the input string
@@ -75,7 +146,7 @@ macro_rules! match_ignore_ascii_case {
75146/// # fn main() {} // Make doctest not wrap everything in its own main
76147///
77148/// fn color_rgb(input: &str) -> Option<(u8, u8, u8)> {
78- /// cssparser::ascii_case_insensitive_phf_map ! {
149+ /// cssparser::ascii_case_insensitive_map ! {
79150/// keywords -> (u8, u8, u8) = {
80151/// "red" => (255, 0, 0),
81152/// "green" => (0, 255, 0),
@@ -87,6 +158,15 @@ macro_rules! match_ignore_ascii_case {
87158/// ```
88159///
89160/// You can also iterate over the map entries by using `keywords::entries()`.
161+ macro_rules! ascii_case_insensitive_map {
162+ ( $( $any: tt) +) => {
163+ $crate:: ascii_case_insensitive_phf_map!( $( $any) +) ;
164+ } ;
165+ }
166+
167+ /// Fast implementation of `ascii_case_insensitive_map!` using a phf map.
168+ /// See `ascii_case_insensitive_map!` above for docs
169+ #[ cfg( feature = "fast_match_color" ) ]
90170#[ macro_export]
91171macro_rules! ascii_case_insensitive_phf_map {
92172 ( $name: ident -> $ValueType: ty = { $( $key: tt => $value: expr ) ,+ } ) => {
@@ -95,14 +175,22 @@ macro_rules! ascii_case_insensitive_phf_map {
95175 ( $name: ident -> $ValueType: ty = { $( $key: tt => $value: expr, ) + } ) => {
96176 use $crate:: _cssparser_internal_phf as phf;
97177
98- // See macro above for context.
99- mod cssparser_internal {
100- $crate:: _cssparser_internal_max_len! {
101- $( $key ) +
178+ #[ inline( always) ]
179+ const fn const_usize_max( a: usize , b: usize ) -> usize {
180+ if a > b {
181+ a
182+ } else {
183+ b
102184 }
103185 }
104186
105- static MAP : phf:: Map <& ' static str , $ValueType> = phf:: phf_map! {
187+ const MAX_LENGTH : usize = {
188+ let mut maxlen : usize = 0 ;
189+ $( maxlen = const_usize_max( maxlen, ( $key) . len( ) ) ; ) +
190+ maxlen
191+ } ;
192+
193+ static __MAP: phf:: Map <& ' static str , $ValueType> = phf:: phf_map! {
106194 $(
107195 $key => $value,
108196 ) *
@@ -118,12 +206,12 @@ macro_rules! ascii_case_insensitive_phf_map {
118206 impl $name {
119207 #[ allow( dead_code) ]
120208 fn entries( ) -> impl Iterator <Item = ( & ' static & ' static str , & ' static $ValueType) > {
121- MAP . entries( )
209+ __MAP . entries( )
122210 }
123211
124212 fn get( input: & str ) -> Option <& ' static $ValueType> {
125- $crate:: _cssparser_internal_to_lowercase!( input, cssparser_internal :: MAX_LENGTH => lowercase) ;
126- MAP . get( lowercase?)
213+ $crate:: _cssparser_internal_to_lowercase!( input, MAX_LENGTH => lowercase) ;
214+ __MAP . get( lowercase?)
127215 }
128216 }
129217 }
0 commit comments