#![recursion_limit = "128"]
#![deny(warnings)]
extern crate proc_macro;
use std::collections::HashSet;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse_macro_input, spanned::Spanned, Attribute, Data, DeriveInput, Fields, Ident};
use error::Error;
use crate::error::ErrorKind;
mod error;
mod parser;
#[proc_macro_derive(Gui, attributes(imgui))]
pub fn ui_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
match impl_derive(&input) {
Ok(output) => output.into(),
Err(error) => error.to_compile_error().into(),
}
}
fn impl_derive(input: &DeriveInput) -> Result<TokenStream, Error> {
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let (body, catch_fields, catch_methods) = match input.data {
Data::Struct(ref body) => struct_body(body.fields.clone()),
_ => Err(Error::non_struct(input.span())),
}?;
let event_type = Ident::new(&format!("__{}_Events", name.to_string()), input.span());
Ok(quote! {
#[allow(non_camel_case_types)]
pub struct #event_type {
#catch_fields
}
impl #event_type {
#catch_methods
}
impl #impl_generics imgui_ext::Gui for #name #ty_generics #where_clause {
type Events = #event_type;
fn draw_gui(ui: &imgui::Ui, ext: &mut Self) -> Self::Events {
let mut events: Self::Events = unsafe { std::mem::zeroed() };
#body
events
}
}
})
}
fn struct_body(fields: Fields) -> Result<(TokenStream, TokenStream, TokenStream), Error> {
let mut input_methods: TokenStream = TokenStream::new();
let mut input_fields: TokenStream = TokenStream::new();
let mut input_fields_set = HashSet::new();
let field_body = fields
.iter()
.enumerate()
.flat_map(|(_, field)| {
let ident = field
.ident
.clone()
.expect("Unnamed fields not yet supported.");
let ty = &field.ty;
let attrs: Vec<Attribute> = field
.attrs
.iter()
.filter(|attr| {
let ident = Ident::new("imgui", attr.span());
attr.path.is_ident(&ident)
})
.cloned()
.collect();
let mut attrs = attrs.into_iter();
let first = attrs.next();
let second = attrs.next();
match (first, second) {
(None, None) => vec![Ok(TokenStream::new())],
(Some(_), Some(err)) => vec![Err(Error::multiple(err.span()))],
(Some(attr), None) => {
let tags = attr
.parse_meta() .map_err(|_| Error::new(ErrorKind::ParseError, attr.span()))
.and_then(parser::parse_meta);
match tags {
Err(error) => vec![Err(error)],
Ok(tags) => tags
.into_iter()
.map(|tag| {
parser::emmit_tag_tokens(
&ident,
&ty,
&attr,
&tag,
&mut input_fields,
&mut input_methods,
&mut input_fields_set,
)
})
.collect(),
}
}
_ => unreachable!(),
}
})
.collect::<Result<Vec<_>, Error>>()?;
Ok((quote! { #( #field_body );*}, input_fields, input_methods))
}