#[macro_use]
extern crate darling;
extern crate proc_macro2;
#[macro_use]
extern crate quote;
extern crate syn;
use darling::ast;
use darling::FromDeriveInput;
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::parse_str;
#[derive(Debug, Clone, Copy, FromMeta)]
#[darling(default)]
enum Volume {
Normal,
Whisper,
Shout,
}
impl Default for Volume {
fn default() -> Self {
Volume::Normal
}
}
#[derive(Debug, FromDeriveInput)]
#[darling(attributes(my_trait), supports(struct_any))]
struct MyInputReceiver {
ident: syn::Ident,
generics: syn::Generics,
data: ast::Data<(), MyFieldReceiver>,
#[darling(default)]
volume: Volume,
}
impl ToTokens for MyInputReceiver {
fn to_tokens(&self, tokens: &mut TokenStream) {
let MyInputReceiver {
ref ident,
ref generics,
ref data,
volume,
} = *self;
let (imp, ty, wher) = generics.split_for_impl();
let fields = data.as_ref()
.take_struct()
.expect("Should never be enum")
.fields;
let fmt_string = fields
.iter()
.enumerate()
.map(|(i, f)| {
format!(
"{} = {{}}",
f.ident
.as_ref()
.map(|v| format!("{}", v))
.unwrap_or_else(|| format!("{}", i))
)
})
.collect::<Vec<_>>()
.join(", ");
let field_list = fields
.into_iter()
.enumerate()
.map(|(i, f)| {
let field_volume = f.volume.unwrap_or(volume);
let field_ident = f.ident
.as_ref()
.map(|v| quote!(#v))
.unwrap_or_else(|| quote!(#i));
match field_volume {
Volume::Normal => quote!(self.#field_ident),
Volume::Shout => {
quote!(::std::string::ToString::to_string(&self.#field_ident).to_uppercase())
}
Volume::Whisper => {
quote!(::std::string::ToString::to_string(&self.#field_ident).to_lowercase())
}
}
})
.collect::<Vec<_>>();
tokens.extend(quote! {
impl #imp Speak for #ident #ty #wher {
fn speak(&self, writer: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(#fmt_string, #(#field_list),*)
}
}
});
}
}
#[derive(Debug, FromField)]
#[darling(attributes(my_trait))]
struct MyFieldReceiver {
ident: Option<syn::Ident>,
ty: syn::Type,
#[darling(default)]
volume: Option<Volume>,
}
fn main() {
let input = r#"#[derive(MyTrait)]
#[my_trait(volume = "shout")]
pub struct Foo {
#[my_trait(volume = "whisper")]
bar: bool,
baz: i64,
}"#;
let parsed = parse_str(input).unwrap();
let receiver = MyInputReceiver::from_derive_input(&parsed).unwrap();
let tokens = quote!(#receiver);
println!(
r#"
INPUT:
{}
PARSED AS:
{:?}
EMITS:
{}
"#,
input, receiver, tokens
);
}