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 _carbide::widget::Common for #ident #ty_generics #where_clause {
fn common(&self) -> &_carbide::widget::CommonBuilder {
&self.#common_field
}
fn common_mut(&mut self) -> &mut _carbide::widget::CommonBuilder {
&mut self.#common_field
}
}
};
quote! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const #dummy_const: () = {
extern crate carbide_core as _carbide;
#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_carbide=false;
let mut has_common_builder = false;
if let syn::Meta::List(_metalist) = _meta {
if _metalist.path.is_ident("carbide") {
is_carbide = 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_carbide && 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 #[carbide(common_builder)] attributes when only one is required",
Error::UnnamedCommonBuilderField =>
"Cannot use #[carbide(common_builder)] attribute on unnamed fields",
Error::NoCommonBuilderField =>
"`#[derive(WidgetCommon)]` requires a struct with one field of type \
`carbide::widget::CommonBuilder` that has the `#[carbide(common_builder)]` \
attribute",
};
write!(f, "{}", s)
}
}