Skip to content

Commit 0192030

Browse files
committed
Add a ascii_case_insensitive_phf_map macro, use it for color keywords.
Fix #115.
1 parent 808922a commit 0192030

File tree

5 files changed

+288
-189
lines changed

5 files changed

+288
-189
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ encoding_rs = "0.5"
2626
cssparser-macros = {path = "./macros", version = "0.1"}
2727
heapsize = {version = "0.3", optional = true}
2828
matches = "0.1"
29+
phf = "0.7"
2930
serde = {version = "0.9", optional = true}
3031

3132
[build-dependencies]

macros/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,7 @@ path = "lib.rs"
1212
proc-macro = true
1313

1414
[dependencies]
15-
syn = "0.11"
15+
phf_codegen = "0.7"
1616
quote = "0.3"
17+
syn = "0.11"
18+

macros/lib.rs

Lines changed: 75 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,96 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5+
extern crate phf_codegen;
56
extern crate proc_macro;
67
#[macro_use] extern crate quote;
78
extern crate syn;
89

910
use std::ascii::AsciiExt;
1011

11-
#[proc_macro_derive(cssparser__match_ignore_ascii_case__derive,
12+
#[proc_macro_derive(cssparser__match_ignore_ascii_case__max_len,
1213
attributes(cssparser__match_ignore_ascii_case__data))]
13-
pub fn expand_token_stream(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
14+
pub fn max_len(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1415
let input = syn::parse_macro_input(&input.to_string()).unwrap();
16+
let data = list_attr(&input, "cssparser__match_ignore_ascii_case__data");
1517

16-
let max_length;
18+
let lengths = data.iter().map(|sub_attr| {
19+
let string = sub_attr_value(sub_attr, "string");
20+
assert_eq!(*string, string.to_ascii_lowercase(),
21+
"the expected strings must be given in ASCII lowercase");
22+
string.len()
23+
});
24+
let max_length = lengths.max().expect("expected at least one string");
1725

18-
match input.attrs[0].value {
19-
syn::MetaItem::List(ref ident, ref nested)
20-
if ident == "cssparser__match_ignore_ascii_case__data" => {
21-
let lengths = nested.iter().map(|sub_attr| match *sub_attr {
22-
syn::NestedMetaItem::MetaItem(
23-
syn::MetaItem::NameValue(ref ident, syn::Lit::Str(ref string, _))
24-
)
25-
if ident == "string" => {
26-
assert_eq!(*string, string.to_ascii_lowercase(),
27-
"the expected strings must be given in ASCII lowercase");
28-
string.len()
29-
}
30-
_ => {
31-
panic!("expected a `string = \"\" parameter to the attribute, got {:?}", sub_attr)
32-
}
33-
});
34-
35-
max_length = lengths.max().expect("expected at least one string")
36-
}
37-
_ => {
38-
panic!("expected a cssparser_match_ignore_ascii_case_data attribute")
26+
let tokens = quote! {
27+
const MAX_LENGTH: usize = #max_length;
28+
};
29+
30+
tokens.as_str().parse().unwrap()
31+
}
32+
33+
34+
#[proc_macro_derive(cssparser__phf_map,
35+
attributes(cssparser__phf_map__kv_pairs))]
36+
pub fn phf_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
37+
let input = syn::parse_macro_input(&input.to_string()).unwrap();
38+
let name = &input.ident;
39+
let value_type = match input.body {
40+
syn::Body::Struct(syn::VariantData::Tuple(ref fields)) if fields.len() == 1 => {
41+
&fields[0].ty
3942
}
43+
_ => panic!("expected tuple struct newtype, got {:?}", input.body)
44+
};
45+
46+
let kv_pairs = list_attr(&input, "cssparser__phf_map__kv_pairs");
47+
48+
let mut map = phf_codegen::Map::new();
49+
for chunk in kv_pairs.chunks(2) {
50+
let key = sub_attr_value(&chunk[0], "key");
51+
let value = sub_attr_value(&chunk[1], "value");
52+
map.entry(key, value);
4053
}
4154

55+
let mut initializer_bytes = Vec::<u8>::new();
56+
let mut initializer_tokens = quote::Tokens::new();
57+
map.build(&mut initializer_bytes).unwrap();
58+
initializer_tokens.append(::std::str::from_utf8(&initializer_bytes).unwrap());
59+
4260
let tokens = quote! {
43-
const MAX_LENGTH: usize = #max_length;
61+
impl #name {
62+
#[inline]
63+
fn map() -> &'static ::phf::Map<&'static str, #value_type> {
64+
static MAP: ::phf::Map<&'static str, #value_type> = #initializer_tokens;
65+
&MAP
66+
}
67+
}
4468
};
4569

4670
tokens.as_str().parse().unwrap()
4771
}
72+
73+
fn list_attr<'a>(input: &'a syn::DeriveInput, expected_name: &str) -> &'a [syn::NestedMetaItem] {
74+
match input.attrs[0].value {
75+
syn::MetaItem::List(ref name, ref nested) if name == expected_name => {
76+
nested
77+
}
78+
_ => {
79+
panic!("expected a {} attribute", expected_name)
80+
}
81+
}
82+
}
83+
84+
fn sub_attr_value<'a>(sub_attr: &'a syn::NestedMetaItem, expected_name: &str) -> &'a str {
85+
match *sub_attr {
86+
syn::NestedMetaItem::MetaItem(
87+
syn::MetaItem::NameValue(ref name, syn::Lit::Str(ref value, _))
88+
)
89+
if name == expected_name => {
90+
value
91+
}
92+
_ => {
93+
panic!("expected a `{} = \"\"` parameter to the attribute, got {:?}",
94+
expected_name, sub_attr)
95+
}
96+
}
97+
}

0 commit comments

Comments
 (0)