use std;
use proc_macro2;
use syn;
pub fn impl_widget_common(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
let ident = &ast.ident;
let common_field = common_builder_field(ast).unwrap();
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let dummy_const = syn::Ident::new(
&format!("_IMPL_WIDGET_COMMON_FOR_{}", ident),
proc_macro2::Span::call_site(),
);
let impl_item = quote! {
impl #impl_generics _conrod::widget::Common for #ident #ty_generics #where_clause {
fn common(&self) -> &_conrod::widget::CommonBuilder {
&self.#common_field
}
fn common_mut(&mut self) -> &mut _conrod::widget::CommonBuilder {
&mut self.#common_field
}
}
};
quote! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const #dummy_const: () = {
extern crate conrod_core as _conrod;
#impl_item
};
}
}
pub fn impl_widget_common_(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
let ident = &ast.ident;
let common_field = common_builder_field(ast).unwrap();
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let dummy_const = syn::Ident::new(
&format!("_IMPL_WIDGET_COMMON_FOR_{}", ident),
proc_macro2::Span::call_site(),
);
let impl_item = quote! {
impl #impl_generics ::widget::Common for #ident #ty_generics #where_clause {
fn common(&self) -> &::widget::CommonBuilder {
&self.#common_field
}
fn common_mut(&mut self) -> &mut ::widget::CommonBuilder {
&mut self.#common_field
}
}
};
quote! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const #dummy_const: () = {
#impl_item
};
}
}
fn common_builder_field(ast: &syn::DeriveInput) -> Result<&syn::Ident, Error> {
let body = match ast.data {
syn::Data::Struct(ref body) => body,
_ => return Err(Error::NotStruct),
};
match body.fields {
syn::Fields::Named(_) => {}
syn::Fields::Unnamed(_) => return Err(Error::TupleStruct),
syn::Fields::Unit => return Err(Error::UnitStruct),
};
let mut common_field = None;
for field in body.fields.iter() {
for attr in &field.attrs {
if let Ok(_meta) = attr.parse_meta() {
let mut is_conrod = false;
let mut has_common_builder = false;
if let syn::Meta::List(_metalist) = _meta {
if _metalist.path.is_ident("conrod") {
is_conrod = true;
}
has_common_builder = _metalist.nested.iter().any(|v| match *v {
syn::NestedMeta::Meta(syn::Meta::Path(ref p))
if p.is_ident("common_builder") =>
{
true
}
_ => false,
});
}
if is_conrod && has_common_builder {
if common_field.is_some() {
return Err(Error::MultipleCommonBuilderFields);
}
common_field = match field.ident.as_ref() {
Some(ident) => Some(ident),
None => return Err(Error::UnnamedCommonBuilderField),
};
}
}
}
}
let common_field = match common_field {
Some(field) => field,
None => return Err(Error::NoCommonBuilderField),
};
Ok(common_field)
}
#[derive(Debug)]
enum Error {
NotStruct,
TupleStruct,
UnitStruct,
MultipleCommonBuilderFields,
UnnamedCommonBuilderField,
NoCommonBuilderField,
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let s = match *self {
Error::NotStruct => "#[derive(WidgetCommon)] is only defined for structs",
Error::TupleStruct => "#[derive(WidgetCommon)] is not defined for tuple structs",
Error::UnitStruct => "#[derive(WidgetCommon)] is not defined for unit structs",
Error::MultipleCommonBuilderFields => {
"Found multiple #[conrod(common_builder)] attributes when only one is required"
}
Error::UnnamedCommonBuilderField => {
"Cannot use #[conrod(common_builder)] attribute on unnamed fields"
}
Error::NoCommonBuilderField => {
"`#[derive(WidgetCommon)]` requires a struct with one field of type \
`conrod::widget::CommonBuilder` that has the `#[conrod(common_builder)]` \
attribute"
}
};
write!(f, "{}", s)
}
}