#![recursion_limit = "128"]
extern crate proc_macro;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro::TokenStream;
#[doc(hidden)]
#[proc_macro_derive(DeserializeTryFrom, attributes(deserialize_from))]
pub fn derive_deserialize_try_from(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
deserialize_from_impl(ast, true).into()
}
#[doc(hidden)]
#[proc_macro_derive(DeserializeFrom, attributes(deserialize_from))]
pub fn derive_deserialize_from(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
deserialize_from_impl(ast, false).into()
}
fn deserialize_from_impl(ast: syn::DeriveInput, try: bool) -> quote::Tokens {
let name = ast.ident;
let base = get_attr_type(&ast, "deserialize_from").unwrap_or_else(|| get_field(&ast).1);
let (_, ty_generics, where_clause) = ast.generics.split_for_impl();
let lifetime = syn::parse(quote! { 'de_derive_serialize_into }.into()).unwrap();
let generics = with_lifetime(&ast.generics, &lifetime);
let (impl_generics, _, _) = generics.split_for_impl();
let body = if try {
quote! {
::try_from::TryInto::try_into(s).map_err(<D::Error as serde::de::Error>::custom)
}
} else {
quote! { Ok(s.into()) }
};
quote! {
impl#impl_generics ::serde::Deserialize<#lifetime> for #name#ty_generics #where_clause {
fn deserialize<D: ::serde::Deserializer<#lifetime>>(deserializer: D)
-> Result<Self, D::Error> {
let s = <#base as ::serde::Deserialize>::deserialize(deserializer)?;
#body
}
}
}
}
#[doc(hidden)]
#[proc_macro_derive(SerializeInto, attributes(serialize_into))]
pub fn derive_serialize_into(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
serialize_into_impl(ast).into()
}
fn serialize_into_impl(ast: syn::DeriveInput) -> quote::Tokens {
let name = ast.ident;
let base: syn::Type = get_attr_type(&ast, "serialize_into").unwrap_or_else(|| {
let raw_base = get_field(&ast).1;
parse_quote! { &#raw_base }
});
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
quote! {
impl#impl_generics ::serde::Serialize for #name#ty_generics #where_clause {
fn serialize<S: ::serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let b: #base = self.into();
::serde::Serialize::serialize(&b, serializer)
}
}
}
}
#[doc(hidden)]
#[proc_macro_derive(IntoInner)]
pub fn derive_into_inner(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
into_inner_impl(ast).into()
}
fn into_inner_impl(ast: syn::DeriveInput) -> quote::Tokens {
let name = ast.ident;
let (field, base) = get_field(&ast);
let (_, ty_generics, where_clause) = ast.generics.split_for_impl();
let lifetime = syn::parse(quote! { 'a_derive_serialize_into }.into()).unwrap();
let generics = with_lifetime(&ast.generics, &lifetime);
let (impl_generics, _, _) = generics.split_for_impl();
let body = match field {
None => quote! { &outer.0 },
Some(ident) => quote! { &outer.#ident },
};
quote! {
impl#impl_generics From<&#lifetime #name#ty_generics> for &#lifetime #base #where_clause {
fn from(outer: &#name#ty_generics) -> &#base {
#body
}
}
}
}
#[doc(hidden)]
#[proc_macro_derive(FromInner)]
pub fn derive_from_inner(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
from_inner_impl(ast).into()
}
fn from_inner_impl(ast: syn::DeriveInput) -> quote::Tokens {
let name = ast.ident;
let (field, base) = get_field(&ast);
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let body = match field {
None => quote! { #name(inner) },
Some(ident) => quote! { #name { #ident: inner } },
};
quote! {
impl#impl_generics From<#base> for #name#ty_generics #where_clause {
fn from(inner: #base) -> #name#ty_generics {
#body
}
}
}
}
#[doc(hidden)]
#[proc_macro_derive(TryFromInner, attributes(try_from_inner, try_from_inner_regex))]
pub fn derive_try_from_inner(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
try_from_inner_impl(ast).into()
}
fn try_from_inner_impl(ast: syn::DeriveInput) -> quote::Tokens {
let name = ast.ident;
let check = get_attr_try_from_inner(&ast);
let (field, base) = get_field(&ast);
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let construct = match field {
None => quote! { #name(inner) },
Some(ident) => quote! { #name { #ident: inner } },
};
let fn_body = match check {
TryFromInnerCheck::BoolFn(path) => quote! {
if #path(&inner) {
Ok(#construct)
} else {
Err("validation function returned false")
}
},
TryFromInnerCheck::Regex(regex) => quote! {
lazy_static! {
static ref RE: ::regex::Regex = ::regex::Regex::new(#regex).expect("invalid regex");
}
if RE.is_match(&inner) {
Ok(#construct)
} else {
Err("value does not match regular expression")
}
},
};
quote! {
impl#impl_generics ::try_from::TryFrom<#base> for #name#ty_generics #where_clause {
type Err = &'static str;
fn try_from(inner: #base) -> ::std::result::Result<#name#ty_generics, &'static str> {
#fn_body
}
}
}
}
fn with_lifetime(generics: &syn::Generics, lifetime: &syn::LifetimeDef) -> syn::Generics {
let mut new_generics = generics.clone();
new_generics.params.insert(0, lifetime.clone().into());
new_generics
}
enum TryFromInnerCheck {
BoolFn(syn::ExprPath),
Regex(syn::LitStr),
}
fn get_attr_try_from_inner(ast: &syn::DeriveInput) -> TryFromInnerCheck {
let bool_fn = map_unique_attr(ast, "try_from_inner", |meta| {
let nv = match meta {
syn::Meta::NameValue(nv) => nv,
_ => panic!("try_from_inner attribute requires a name-value pair"),
};
let fn_str = match nv.lit {
syn::Lit::Str(lit_str) => lit_str.value(),
_ => panic!("try_from_inner attribute needs a function name as a string value"),
};
let fn_path = syn::parse_str(&fn_str).expect("try_from_inner argument must be a function");
TryFromInnerCheck::BoolFn(fn_path)
});
let regex = map_unique_attr(ast, "try_from_inner_regex", |meta| {
let nv = match meta {
syn::Meta::NameValue(nv) => nv,
_ => panic!("try_from_inner_regex attribute requires a name-value pair"),
};
match nv.lit {
syn::Lit::Str(lit_str) => TryFromInnerCheck::Regex(lit_str),
_ => panic!("try_from_inner_regex attribute needs a regex as a string value"),
}
});
match (bool_fn, regex) {
(None, None) => panic!("try_from_inner or try_from_inner_regex attribute missing"),
(Some(_), Some(_)) => panic!("only one of try_from_inner and try_from_inner_regex allowed"),
(Some(bool_fn), None) => bool_fn,
(None, Some(regex)) => regex,
}
}
fn get_attr_type(ast: &syn::DeriveInput, name: &str) -> Option<syn::Type> {
map_unique_attr(ast, name, |meta| match meta {
syn::Meta::Word(_) | syn::Meta::NameValue(_) => {
panic!("Base type attribute must be a single identifier in brackets.");
}
syn::Meta::List(list) => {
if list.nested.len() != 1 {
panic!("Base type attribute must be a single identifier in brackets.");
}
match list.nested.first().unwrap().into_value().clone() {
syn::NestedMeta::Meta(syn::Meta::Word(word)) => syn::TypeVerbatim {
tts: parse_quote! { #word },
}.into(),
_ => panic!("Base type attribute must be a single identifier in brackets."),
}
}
})
}
fn map_unique_attr<T, F>(ast: &syn::DeriveInput, name: &str, f: F) -> Option<T>
where
F: Fn(syn::Meta) -> T,
{
let mut t_iter = ast.attrs
.iter()
.filter_map(syn::Attribute::interpret_meta)
.filter(|meta| meta.name() == name)
.map(f);
match (t_iter.next(), t_iter.next()) {
(_, Some(_)) => panic!("Multiple {} attributes specified.", name),
(result, None) => result,
}
}
fn get_field(ast: &syn::DeriveInput) -> (Option<syn::Ident>, syn::Type) {
match ast.data {
syn::Data::Struct(ref data_struct) => {
let mut fields = data_struct.fields.iter();
match (fields.next(), fields.next()) {
(Some(field), None) => (field.ident.clone(), field.ty.clone()),
_ => panic!("Can only derive base type for structs with a single field."),
}
}
_ => unimplemented!(),
}
}