Files
tubestation/third_party/rust/cssparser-macros/lib.rs
Manish Goregaokar 4ed623c8bb Bug 1342759 - Revendor dependencies r=manishearth
MozReview-Commit-ID: byBQQbHb8J
2017-02-26 04:11:48 -08:00

124 lines
4.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate phf_codegen;
extern crate proc_macro;
#[macro_use] extern crate quote;
extern crate syn;
use std::ascii::AsciiExt;
/// Find a `#[cssparser__assert_ascii_lowercase__data(string = "…", string = "…")]` attribute,
/// and panic if any string contains ASCII uppercase letters.
#[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()
}
/// Find a `#[cssparser__max_len__data(string = "…", string = "…")]` attribute,
/// panic if any string contains ASCII uppercase letters,
/// emit a `MAX_LENGTH` constant with the length of the longest string.
#[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()
}
/// On `struct $Name($ValueType)`, add a new static method
/// `fn map() -> &'static ::phf::Map<&'static str, $ValueType>`.
/// The maps content is given as:
/// `#[cssparser__phf_map__kv_pairs(key = "…", value = "…", key = "…", value = "…")]`.
/// Keys are ASCII-lowercased.
#[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()
}
/// Panic if the first attribute isnt `#[foo(…)]` with the given name,
/// or return the parameters.
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)
}
/// Panic if `sub_attr` is not a name-value like `foo = "…"` with the given name,
/// or return the value.
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)
}
}
}