1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
extern crate phf_codegen;
extern crate proc_macro;
#[macro_use] extern crate quote;
extern crate syn;
use std::ascii::AsciiExt;
#[proc_macro_derive(cssparser__assert_ascii_lowercase,
attributes(cssparser__assert_ascii_lowercase__data))]
pub fn assert_ascii_lowercase(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse_macro_input(&input.to_string()).unwrap();
let data = list_attr(&input, "cssparser__assert_ascii_lowercase__data");
for sub_attr in data {
let string = sub_attr_value(sub_attr, "string");
assert_eq!(*string, string.to_ascii_lowercase(),
"the expected strings must be given in ASCII lowercase");
}
"".parse().unwrap()
}
#[proc_macro_derive(cssparser__max_len,
attributes(cssparser__max_len__data))]
pub fn max_len(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse_macro_input(&input.to_string()).unwrap();
let data = list_attr(&input, "cssparser__max_len__data");
let lengths = data.iter().map(|sub_attr| sub_attr_value(sub_attr, "string").len());
let max_length = lengths.max().expect("expected at least one string");
let tokens = quote! {
const MAX_LENGTH: usize = #max_length;
};
tokens.as_str().parse().unwrap()
}
#[proc_macro_derive(cssparser__phf_map,
attributes(cssparser__phf_map__kv_pairs))]
pub fn phf_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse_macro_input(&input.to_string()).unwrap();
let name = &input.ident;
let value_type = match input.body {
syn::Body::Struct(syn::VariantData::Tuple(ref fields)) if fields.len() == 1 => {
&fields[0].ty
}
_ => panic!("expected tuple struct newtype, got {:?}", input.body)
};
let pairs: Vec<_> = list_attr(&input, "cssparser__phf_map__kv_pairs").chunks(2).map(|chunk| {
let key = sub_attr_value(&chunk[0], "key");
let value = sub_attr_value(&chunk[1], "value");
(key.to_ascii_lowercase(), value)
}).collect();
let mut map = phf_codegen::Map::new();
for &(ref key, value) in &pairs {
map.entry(&**key, value);
}
let mut initializer_bytes = Vec::<u8>::new();
let mut initializer_tokens = quote::Tokens::new();
map.build(&mut initializer_bytes).unwrap();
initializer_tokens.append(::std::str::from_utf8(&initializer_bytes).unwrap());
let tokens = quote! {
impl #name {
#[inline]
fn map() -> &'static ::phf::Map<&'static str, #value_type> {
static MAP: ::phf::Map<&'static str, #value_type> = #initializer_tokens;
&MAP
}
}
};
tokens.as_str().parse().unwrap()
}
fn list_attr<'a>(input: &'a syn::DeriveInput, expected_name: &str) -> &'a [syn::NestedMetaItem] {
for attr in &input.attrs {
match attr.value {
syn::MetaItem::List(ref name, ref nested) if name == expected_name => {
return nested
}
_ => {}
}
}
panic!("expected a {} attribute", expected_name)
}
fn sub_attr_value<'a>(sub_attr: &'a syn::NestedMetaItem, expected_name: &str) -> &'a str {
match *sub_attr {
syn::NestedMetaItem::MetaItem(
syn::MetaItem::NameValue(ref name, syn::Lit::Str(ref value, _))
)
if name == expected_name => {
value
}
_ => {
panic!("expected a `{} = \"…\"` parameter to the attribute, got {:?}",
expected_name, sub_attr)
}
}
}