Skip to content

Commit b0c427d

Browse files
committed
Remove the already-lowercase requirement in ascii_case_insensitive_phf_map!
We can do the lower-casing at compile time in a proc-macro. Note that `match_ignore_ascii_case!` still has that requirement, since it’s `macro_rules!` that generates a `match` expression.
1 parent 983529f commit b0c427d

File tree

2 files changed

+38
-19
lines changed

2 files changed

+38
-19
lines changed

macros/lib.rs

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,33 @@ extern crate syn;
99

1010
use std::ascii::AsciiExt;
1111

12-
/// Find a `#[cssparser__match_ignore_ascii_case__data(string = "…", string = "…")]` attribute,
13-
/// panic if any string contains ASCII uppercase letters,
14-
/// emit a `MAX_LENGTH` constant with the length of the longest string.
15-
#[proc_macro_derive(cssparser__match_ignore_ascii_case__max_len,
16-
attributes(cssparser__match_ignore_ascii_case__data))]
17-
pub fn max_len(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
12+
/// Find a `#[cssparser__assert_ascii_lowercase__data(string = "…", string = "…")]` attribute,
13+
/// and panic if any string contains ASCII uppercase letters.
14+
#[proc_macro_derive(cssparser__assert_ascii_lowercase,
15+
attributes(cssparser__assert_ascii_lowercase__data))]
16+
pub fn assert_ascii_lowercase(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1817
let input = syn::parse_macro_input(&input.to_string()).unwrap();
19-
let data = list_attr(&input, "cssparser__match_ignore_ascii_case__data");
18+
let data = list_attr(&input, "cssparser__assert_ascii_lowercase__data");
2019

21-
let lengths = data.iter().map(|sub_attr| {
20+
for sub_attr in data {
2221
let string = sub_attr_value(sub_attr, "string");
2322
assert_eq!(*string, string.to_ascii_lowercase(),
2423
"the expected strings must be given in ASCII lowercase");
25-
string.len()
26-
});
24+
}
25+
26+
"".parse().unwrap()
27+
}
28+
29+
/// Find a `#[cssparser__max_len__data(string = "…", string = "…")]` attribute,
30+
/// panic if any string contains ASCII uppercase letters,
31+
/// emit a `MAX_LENGTH` constant with the length of the longest string.
32+
#[proc_macro_derive(cssparser__max_len,
33+
attributes(cssparser__max_len__data))]
34+
pub fn max_len(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
35+
let input = syn::parse_macro_input(&input.to_string()).unwrap();
36+
let data = list_attr(&input, "cssparser__max_len__data");
37+
38+
let lengths = data.iter().map(|sub_attr| sub_attr_value(sub_attr, "string").len());
2739
let max_length = lengths.max().expect("expected at least one string");
2840

2941
let tokens = quote! {
@@ -37,6 +49,7 @@ pub fn max_len(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3749
/// `fn map() -> &'static ::phf::Map<&'static str, $ValueType>`.
3850
/// The map’s content is given as:
3951
/// `#[cssparser__phf_map__kv_pairs(key = "…", value = "…", key = "…", value = "…")]`.
52+
/// Keys are ASCII-lowercased.
4053
#[proc_macro_derive(cssparser__phf_map,
4154
attributes(cssparser__phf_map__kv_pairs))]
4255
pub fn phf_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
@@ -49,13 +62,15 @@ pub fn phf_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
4962
_ => panic!("expected tuple struct newtype, got {:?}", input.body)
5063
};
5164

52-
let kv_pairs = list_attr(&input, "cssparser__phf_map__kv_pairs");
53-
54-
let mut map = phf_codegen::Map::new();
55-
for chunk in kv_pairs.chunks(2) {
65+
let pairs: Vec<_> = list_attr(&input, "cssparser__phf_map__kv_pairs").chunks(2).map(|chunk| {
5666
let key = sub_attr_value(&chunk[0], "key");
5767
let value = sub_attr_value(&chunk[1], "value");
58-
map.entry(key, value);
68+
(key.to_ascii_lowercase(), value)
69+
}).collect();
70+
71+
let mut map = phf_codegen::Map::new();
72+
for &(ref key, value) in &pairs {
73+
map.entry(&**key, value);
5974
}
6075

6176
let mut initializer_bytes = Vec::<u8>::new();

src/lib.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ macro_rules! match_ignore_ascii_case {
134134
// finished parsing
135135
(@inner $value:expr, () -> ($(($string:expr => $result:expr))*) $fallback:expr ) => {
136136
{
137+
#[derive(cssparser__assert_ascii_lowercase)]
138+
#[cssparser__assert_ascii_lowercase__data($(string = $string),+)]
139+
#[allow(dead_code)]
140+
struct Dummy;
141+
137142
_cssparser_internal__max_len!($value => lowercase, $($string),+);
138143
match lowercase {
139144
$(
@@ -160,7 +165,6 @@ macro_rules! match_ignore_ascii_case {
160165
/// Requirements:
161166
///
162167
/// * The `phf` and `cssparser_macros` crates must also be imported at the crate root
163-
/// * The keys must not contain ASCII upper case letters. (They must be already be lower-cased.)
164168
/// * The values must be given a strings that contain Rust syntax for a constant expression.
165169
///
166170
/// ## Example:
@@ -216,10 +220,10 @@ macro_rules! ascii_case_insensitive_phf_map {
216220
#[doc(hidden)]
217221
macro_rules! _cssparser_internal__max_len {
218222
($input: expr => $output: ident, $($string: expr),+) => {
219-
#[derive(cssparser__match_ignore_ascii_case__max_len)]
220-
#[cssparser__match_ignore_ascii_case__data($(string = $string),+)]
223+
#[derive(cssparser__max_len)]
224+
#[cssparser__max_len__data($(string = $string),+)]
221225
#[allow(dead_code)]
222-
struct Dummy;
226+
struct Dummy2;
223227

224228
// MAX_LENGTH is generated by cssparser__match_ignore_ascii_case__max_len
225229
let mut buffer: [u8; MAX_LENGTH] =

0 commit comments

Comments
 (0)