#![recursion_limit = "2048"]
extern crate proc_macro;
extern crate proc_macro2;
extern crate syn;
#[macro_use] extern crate quote;
extern crate case;
extern crate proc_macro_crate;
use proc_macro::TokenStream;
use syn::Ident;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use std::fmt;
use std::io::Write;
use quote::TokenStreamExt;
use proc_macro2::{Spacing, Punct};
use case::CaseExt;
use proc_macro_crate::crate_name;
fn _rustfmt<C: fmt::Display>(code: C) -> String{
let code = format!("{}", code);
let mut child = ::std::process::Command::new("rustfmt")
.stdin(::std::process::Stdio::piped())
.stdout(::std::process::Stdio::piped())
.spawn()
.expect("Failed to format source snippet");
{
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
stdin.write_all(code.as_bytes()).expect("Failed to write to stdin");
}
let out = child.wait_with_output().expect("Failed to format source snippet").stdout;
String::from_utf8_lossy(&out).to_string()
}
#[proc_macro_derive(ParameterGroup, attributes(gui))]
pub fn parameter_group(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let vis = ast.vis;
let ident = ast.ident;
let generics = ast.generics;
let data = ast.data;
let attrs = ast.attrs.into_iter().filter(|attr| attr.path.is_ident("gui")).collect::<Vec<_>>();
let input = if let syn::Data::Struct(data_struct) = data {
let fields = data_struct.fields;
quote!(#( #attrs )* #vis struct #ident #generics #fields).into()
}else{
panic!("ParameterGroup can only be derived for structs")
};
let parameter_group = syn::parse_macro_input!(input as ParameterGroup);
parameter_group.impl_parameter_group().into()
}
struct Name{
_ident: Ident,
_equals_token: syn::Token![=],
param_name: syn::Lit,
}
impl syn::parse::Parse for Name{
fn parse(input: syn::parse::ParseStream) -> syn::Result<Name>{
Ok(Name{
_ident: input.parse()?,
_equals_token: input.parse()?,
param_name: input.parse()
.map_err(|err| syn::Error::new(err.span(), "Parsing field name literal"))?,
})
}
}
impl Name{
fn to_string(&self) -> syn::Result<String>{
match &self.param_name{
syn::Lit::Str(lit_str) => Ok(lit_str.value()),
_ => Err(syn::Error::new(self.param_name.span(), "Parameter name should be a string literal")),
}
}
}
struct AsControl{
_ident: Ident,
_equals_token: syn::Token![=],
ty: syn::Type,
}
impl syn::parse::Parse for AsControl{
fn parse(input: syn::parse::ParseStream) -> syn::Result<AsControl>{
Ok(AsControl{
_ident: input.parse()?,
_equals_token: input.parse()?,
ty: input.parse()
.map_err(|err| syn::Error::new(err.span(), "Parsing field as_control type"))?,
})
}
}
struct StyleValue{
ident: syn::Ident,
_colon_token: syn::Token![:],
expr: syn::Expr,
}
impl quote::ToTokens for StyleValue{
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream){
self.ident.to_tokens(tokens);
tokens.append(Punct::new(':', Spacing::Joint));
self.expr.to_tokens(tokens);
}
}
impl syn::parse::Parse for StyleValue{
fn parse(input: syn::parse::ParseStream) -> syn::Result<StyleValue>{
let ident = input.parse()?;
let _colon_token = input.parse()?;
let expr = input.parse()?;
Ok(StyleValue{
ident,
_colon_token,
expr,
})
}
}
impl StyleValue{
fn to_opt(&self) -> proc_macro2::TokenStream {
let ident = &self.ident;
let expr = &self.expr;
quote!( #ident: Some( #expr.into() ) )
}
fn to_set_opt(&self) -> proc_macro2::TokenStream {
let ident = &self.ident;
let expr = &self.expr;
quote!( style.#ident = Some(#expr.into()); )
}
}
struct Style{
_ident: Ident,
_brace: syn::token::Brace,
fields: syn::punctuated::Punctuated<StyleValue, syn::Token![,]>
}
impl syn::parse::Parse for Style{
fn parse(input: syn::parse::ParseStream) -> syn::Result<Style>{
let _ident = input.parse()?;
let content;
let _brace = syn::braced!(content in input);
let fields = syn::punctuated::Punctuated::parse_terminated(&content)?;
Ok(Style{
_ident,
_brace,
fields,
})
}
}
impl quote::ToTokens for Style {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream){
let fields = &self.fields;
let group = proc_macro2::Group::new(proc_macro2::Delimiter::Brace, quote!(#fields));
tokens.append(group);
}
}
impl Style{
fn to_ringui_style(&self, ringui_style: syn::Path) -> proc_macro2::TokenStream {
let fields = self.fields.iter().map(|style_value| style_value.to_opt());
quote!{
#ringui_style{
#( #fields, )*
.. Default::default()
}
}
}
fn to_set_fields(&self) -> proc_macro2::TokenStream {
let set_field = self.fields.iter().map(|style_value| style_value.to_set_opt());
quote!{
#( #set_field )*
}
}
}
enum ParameterAttributeTts{
Name(Name),
NotParameter(Ident),
AsControl(AsControl),
Style(Style),
Disable(Ident),
Hide(Ident),
NoSerialize(Ident),
}
impl syn::parse::Parse for ParameterAttributeTts{
fn parse(input: syn::parse::ParseStream) -> syn::Result<ParameterAttributeTts>{
let cursor = input.cursor();
if let Some((path,_)) = cursor.ident(){
if path == "name" {
let name = input.parse()?;
Ok(ParameterAttributeTts::Name(name))
}else if path == "not_parameter" {
let ident = input.parse()?;
Ok(ParameterAttributeTts::NotParameter(ident))
}else if path == "disable" {
let ident = input.parse()?;
Ok(ParameterAttributeTts::Disable(ident))
}else if path == "hide" {
let ident = input.parse()?;
Ok(ParameterAttributeTts::Hide(ident))
}else if path == "as_control" {
let control = input.parse()?;
Ok(ParameterAttributeTts::AsControl(control))
}else if path == "style" {
let style = input.parse()?;
Ok(ParameterAttributeTts::Style(style))
}else if path == "no_serialize"{
let ident = input.parse()?;
Ok(ParameterAttributeTts::NoSerialize(ident))
}else{
Err(input.error("Error parsing Parameter attribute not supported"))
}
}else{
Err(input.error("Expecting identifier"))
}
}
}
struct GuiAttribute{
pub pound_token: syn::token::Pound,
pub bracket_token: syn::token::Bracket,
pub gui_path: syn::Path,
pub paren: Option<syn::token::Paren>,
pub attributes: Punctuated<ParameterAttributeTts, syn::Token![,]>,
}
impl syn::parse::Parse for GuiAttribute{
fn parse(input: syn::parse::ParseStream) -> syn::Result<GuiAttribute>{
let content;
let pound_token = input.parse()?;
let bracket_token = syn::bracketed!(content in input);
let gui_path: syn::Path = content.parse()?;
let paren;
let attributes;
if gui_path.is_ident("gui"){
let attr_content;
paren = Some(syn::parenthesized!(attr_content in content));
attributes = Punctuated::parse_terminated(&attr_content)?;
}else{
content.cursor().token_stream();
paren = None;
attributes = Punctuated::new();
}
Ok(GuiAttribute{
pound_token,
bracket_token,
gui_path,
paren,
attributes,
})
}
}
impl GuiAttribute{
fn parse_outer(input: syn::parse::ParseStream) -> syn::Result<Vec<Self>>{
let attrs = syn::Attribute::parse_outer(input)?
.into_iter()
.filter(|attr| attr.path.is_ident("gui"))
.collect::<Vec<_>>();
let mut mat_attrs = Vec::new();
for attr in attrs {
let input = quote!(#attr);
mat_attrs.push(syn::parse2(input)?);
}
Ok(mat_attrs)
}
}
struct Parameter{
attrs: Vec<GuiAttribute>,
_vis: syn::Visibility,
ident: syn::Ident,
_colon_token: syn::Token![:],
ty: syn::Type
}
impl Parameter{
fn is_parameter(&self) -> bool{
!self.attrs.iter().any(|attr|
attr.attributes.iter()
.any(|attr| if let ParameterAttributeTts::NotParameter(_) = attr{
true
}else{
false
}))
}
fn is_mut_parameter(&self) -> bool{
if let syn::Type::Path(type_path) = &self.ty{
let ty_name = type_path.path.segments.last().unwrap().ident.to_string();
ty_name.starts_with("Property") || ty_name.starts_with("RangedPropertyMut")
}else{
false
}
}
fn disable_control(&self) -> bool {
self.attrs.iter().any(|attr|
attr.attributes.iter()
.any(|attr| if let ParameterAttributeTts::Disable(_) = attr{
true
}else{
false
}))
}
fn hide_control(&self) -> bool {
self.attrs.iter().any(|attr|
attr.attributes.iter()
.any(|attr| if let ParameterAttributeTts::Hide(_) = attr{
true
}else{
false
}))
}
fn serialize_control(&self) -> bool {
!self.attrs.iter().any(|attr|
attr.attributes.iter()
.any(|attr| if let ParameterAttributeTts::NoSerialize(_) = attr{
true
}else{
false
}))
}
fn name(&self) -> syn::Result<String>{
let mut names = self.attrs.iter().flat_map(|attrs| attrs.attributes.iter())
.filter_map(|attr| if let ParameterAttributeTts::Name(name) = attr{
Some(name.to_string())
}else{
None
});
let name = names.next();
if names.next().is_some() {
Err(syn::Error::new(self.ident.span(), "Parameter has more than one name attribute"))
}else{
name.unwrap_or_else(|| Ok(self.ident.to_string().replace("_", " ")))
}
}
fn parameter_set(&self) -> proc_macro2::TokenStream {
let field_name = &self.ident;
quote!{
self.#field_name.set(other.#field_name.get().clone());
}
}
fn parameter_add(&self) -> syn::Result<proc_macro2::TokenStream> {
let mut as_controls = self.attrs.iter().flat_map(|attrs| attrs.attributes.iter())
.filter_map(|attr| if let ParameterAttributeTts::AsControl(control) = attr{
Some(control)
}else{
None
});
let as_control = as_controls.next();
if let Some(as_control) = as_controls.next() {
return Err(syn::Error::new(as_control._ident.span(), "Parameter has more than one as_control attribute"))
}
let mut styles = self.attrs.iter().flat_map(|attrs| attrs.attributes.iter())
.filter_map(|attr| if let ParameterAttributeTts::Style(style) = attr{
Some(style)
}else{
None
});
let style = styles.next();
if let Some(style) = styles.next() {
return Err(syn::Error::new(style._ident.span(), "Parameter has more than one style attribute"))
}
let field_name = self.name()?;
let field = &self.ident;
let gui_module_name_str = crate_name("rin-gui")
.unwrap_or("rin::gui".to_owned());
let gui_module_name: syn::Path = syn::parse_str(&gui_module_name_str).unwrap();
let disable = self.disable_control();
let hide = self.hide_control();
let serialize = self.serialize_control();
if let (Some(as_control), Some(style)) = (as_control, style) {
let as_control = &as_control.ty;
let ty = &self.ty;
let style_ty = quote!{
<<#as_control as #gui_module_name::ControlBuilt<#ty>>::Builder as #gui_module_name::ControlBuilderWithStyle>::Style
};
let set_style = style.to_set_fields();
Ok(quote!{
let mut style = #style_ty::default();
#set_style
let as_control = #gui_module_name::As::<#as_control>::new(self.#field.clone());
let control = if #serialize {
group.new_control(#field_name, #gui_module_name::styled(as_control, style))
}else{
group.new_control_no_serialize(#field_name, #gui_module_name::styled(as_control, style))
};
if #disable {
#gui_module_name::ControlEvents::disable(control);
}
if #hide {
#gui_module_name::ControlEvents::hide(control);
}
})
}else if let Some(as_control) = as_control {
let as_control = &as_control.ty;
Ok(quote!{
let control = if #serialize {
group.new_control(#field_name, #gui_module_name::As::<#as_control>::new(self.#field.clone()))
}else{
group.new_control_no_serialize(#field_name, #gui_module_name::As::<#as_control>::new(self.#field.clone()))
};
if #disable {
#gui_module_name::ControlEvents::disable(control);
}
if #hide {
#gui_module_name::ControlEvents::hide(control);
}
})
}else if let Some(style) = style{
let ty = &self.ty;
let style_ty = quote!{
<<#ty as #gui_module_name::ControlDefaultProperty>::Builder as #gui_module_name::ControlBuilderWithStyle>::Style
};
let set_style = style.to_set_fields();
Ok(quote!{
let mut style = #style_ty::default();
#set_style
let control = if #serialize {
group.new_control(#field_name, #gui_module_name::styled(self.#field.clone(), style))
}else{
group.new_control_no_serialize(#field_name, #gui_module_name::styled(self.#field.clone(), style))
};
if #disable {
#gui_module_name::ControlEvents::disable(control);
}
if #hide {
#gui_module_name::ControlEvents::hide(control);
}
})
}else{
Ok(quote!{
let control = if #serialize {
group.new_control(#field_name, self.#field.clone())
}else{
group.new_control_no_serialize(#field_name, self.#field.clone())
};
if #disable {
#gui_module_name::ControlEvents::disable(control);
}
if #hide {
#gui_module_name::ControlEvents::hide(control);
}
})
}
}
}
impl syn::parse::Parse for Parameter{
fn parse(input: syn::parse::ParseStream) -> syn::Result<Parameter>{
Ok(Parameter{
attrs: input.call(GuiAttribute::parse_outer)?,
_vis: input.parse()?,
ident: input.parse()?,
_colon_token: input.parse()?,
ty: input.parse()?,
})
}
}
struct ParameterGroup{
_vis: syn::Visibility,
_struct_token: syn::Token![struct],
ident: syn::Ident,
generics: syn::Generics,
_brace_token: syn::token::Brace,
fields: Punctuated<Parameter, syn::Token![,]>,
}
impl syn::parse::Parse for ParameterGroup{
fn parse(input: syn::parse::ParseStream) -> syn::Result<ParameterGroup>{
syn::Attribute::parse_inner(input)
.map_err(|err| syn::Error::new(err.span(), "Parsing parameter group attribute"))?;
let content;
let _vis = input.parse()
.map_err(|err| syn::Error::new(err.span(), "Parsing struct visibility"))?;
let _struct_token = input.parse()
.map_err(|err| syn::Error::new(err.span(), "Parsing struct token"))?;
let ident = input.parse()
.map_err(|err| syn::Error::new(err.span(), "Parsing struct ident"))?;
let generics = input.parse()
.map_err(|err| syn::Error::new(err.span(), "Parsing struct generics"))?;
let _brace_token = syn::braced!(content in input);
let fields = Punctuated::parse_terminated(&content)
.map_err(|err| syn::Error::new(err.span(), "Parsing struct fields"))?;
Ok(ParameterGroup{
_vis,
_struct_token,
ident,
generics,
_brace_token,
fields,
})
}
}
impl ParameterGroup{
fn impl_parameter_group(&self) -> proc_macro2::TokenStream{
let name = &self.ident;
let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
let add_param = self.fields.iter().filter(|f| f.is_parameter())
.map(|f| match f.parameter_add(){
Ok(param) => param,
Err(err) => err.to_compile_error(),
});
let set_fields = self.fields.iter().filter(|f| f.is_parameter())
.filter_map(|f| if f.is_mut_parameter(){
Some(f.parameter_set())
}else{
None
});
let gui_module_name_str = crate_name("rin-gui")
.unwrap_or("rin::gui".to_owned());
let gui_module_name: syn::Path = syn::parse_str(&gui_module_name_str).unwrap();
quote!{
impl #impl_generics #gui_module_name::ParameterGroup for #name #ty_generics #where_clause{
fn add_parameters<G: #gui_module_name::Group>(&self, group: &mut G){
#(
#add_param
)*
}
fn set(&mut self, other: &Self){
#(
#set_fields
)*
}
}
}
}
}
struct EnumParamVariant{
attrs: Vec<GuiAttribute>,
ident: Ident,
_discriminant: Option<(syn::Token![=], syn::Expr)>
}
impl syn::parse::Parse for EnumParamVariant{
fn parse(input: syn::parse::ParseStream) -> syn::Result<EnumParamVariant>{
let variant: syn::Variant = input.parse()?;
let attrs = variant.attrs.into_iter().filter(|attr| attr.path.is_ident("gui")).map(|attr| {
let attr = format!("{}", quote!(#attr));
syn::parse_str(&attr).unwrap()
}).collect();
Ok(EnumParamVariant{
attrs,
ident: variant.ident,
_discriminant: variant.discriminant,
})
}
}
impl EnumParamVariant{
fn is_enabled(&self) -> bool{
!self.attrs.iter().any(|attr|
attr.attributes.iter()
.any(|attr| if let ParameterAttributeTts::NotParameter(_) = attr{
true
}else{
false
}))
}
fn name(&self) -> syn::Result<proc_macro2::TokenStream> {
let mut names = self.attrs.iter().flat_map(|attrs| attrs.attributes.iter())
.filter_map(|attr| if let ParameterAttributeTts::Name(name) = attr{
Some(name)
}else{
None
});
let name = names.next();
if names.next().is_some() {
return Err(syn::Error::new(self.ident.span(), "Parameter has more than one name attribute"))
}
if let Some(name) = name{
name.to_string().map(|name| quote!( #name ))
}else{
let name = self.ident.to_string().to_snake().replace("_", " ");
Ok(quote!( #name ))
}
}
fn is_usize(&self, enum_name: &syn::Ident) -> proc_macro2::TokenStream {
let variant = &self.ident;
quote!(#enum_name::#variant as usize == v)
}
fn path(&self, enum_name: &syn::Ident) -> proc_macro2::TokenStream {
let variant = &self.ident;
quote!(#enum_name::#variant)
}
fn variant_to_add_control(&self, enum_name: &syn::Ident) -> proc_macro2::TokenStream {
let variant = &self.ident;
let name = match self.name(){
Ok(name) => name,
Err(err) => return err.to_compile_error(),
};
let mut styles = self.attrs.iter().flat_map(|attrs| attrs.attributes.iter())
.filter_map(|attr| if let ParameterAttributeTts::Style(style) = attr{
Some(style)
}else{
None
});
let style = styles.next();
if styles.next().is_some() {
return syn::Error::new(self.ident.span(), "Parameter has more than one style attribute").to_compile_error()
}
let mut as_controls = self.attrs.iter().flat_map(|attrs| attrs.attributes.iter())
.filter_map(|attr| if let ParameterAttributeTts::AsControl(control) = attr{
Some(control)
}else{
None
});
let as_control = as_controls.next();
if as_controls.next().is_some() {
return syn::Error::new(self.ident.span(), "Parameter has more than one as_control attribute").to_compile_error()
}
let gui_module_name_str = crate_name("rin-gui")
.unwrap_or("rin::gui".to_owned());
let gui_module_name: syn::Path = syn::parse_str(&gui_module_name_str).unwrap();
let parameter = if let Some(as_control) = as_control{
let as_control = &as_control.ty;
quote!(#gui_module_name::As::<#as_control>::new(parameter))
}else{
quote!(parameter)
};
let control = if let Some(style) = style{
let toggle_style: syn::Path = if let Some(as_control) = as_control{
let as_control = &as_control.ty;
let style = quote!(
<<#as_control as #gui_module_name::ControlBuilt>::Builder as #gui_module_name::ControlBuilderWithStyle>::Style
);
syn::parse(style.into()).unwrap()
}else{
syn::parse_str(&(gui_module_name_str + "::ToggleStyle")).unwrap()
};
let style = style.to_ringui_style(toggle_style);
quote!{{
let style = if let Some(global_style) = global_style {
global_style.override_style(&#style)
}else{
#style
};
#gui_module_name::styled(#parameter, style)
}}
}else{
quote!{{
if let Some(global_style) = global_style {
#gui_module_name::styled(#parameter, global_style)
}else{
#gui_module_name::styled(#parameter, Default::default())
}
}}
};
quote!(#enum_name::#variant => group.new_control(#name, #control).stream())
}
fn variant_to_enabled(&self, enum_name: &syn::Ident) -> proc_macro2::TokenStream {
let variant = &self.ident;
let enabled = self.is_enabled();
quote!(#enum_name::#variant => #enabled)
}
fn variant_to_name(&self, enum_name: &syn::Ident) -> proc_macro2::TokenStream {
let variant = &self.ident;
let name = match self.name(){
Ok(name) => name,
Err(err) => return err.to_compile_error(),
};
quote!(#enum_name::#variant => #name)
}
}
struct EnumParam{
_vis: syn::Visibility,
_enum_token: syn::Token![enum],
ident: Ident,
generics: syn::Generics,
_brace_token: syn::token::Brace,
variants: Punctuated<EnumParamVariant, syn::Token![,]>,
}
impl syn::parse::Parse for EnumParam{
fn parse(input: syn::parse::ParseStream) -> syn::Result<EnumParam>{
syn::Attribute::parse_inner(input)
.map_err(|err| syn::Error::new(err.span(), "Parsing enum param attribute"))?;
let content;
let _vis = input.parse()
.map_err(|err| syn::Error::new(err.span(), "Parsing enum visibility"))?;
let _enum_token = input.parse()
.map_err(|err| syn::Error::new(err.span(), "Parsing enum token"))?;
let ident = input.parse()
.map_err(|err| syn::Error::new(err.span(), "Parsing enum ident"))?;
let generics = input.parse()
.map_err(|err| syn::Error::new(err.span(), "Parsing enum generics"))?;
let _brace_token = syn::braced!(content in input);
let variants = Punctuated::parse_terminated(&content)
.map_err(|err| syn::Error::new(err.span(), "Parsing enum variants"))?;
Ok(EnumParam{
_vis,
_enum_token,
ident,
generics,
_brace_token,
variants,
})
}
}
impl EnumParam{
fn impl_clike(&self) -> proc_macro2::TokenStream {
let enum_name = &self.ident;
let is_usize = self.variants.iter().map(|variant| variant.is_usize(enum_name));
let path = self.variants.iter().map(|variant| variant.path(enum_name));
let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
let utils_module = crate_name("rin-util")
.unwrap_or("rin::util".to_owned());
let utils_module: syn::Path = syn::parse_str(&utils_module).unwrap();
quote! {
impl #impl_generics #utils_module::CLike for #enum_name #ty_generics #where_clause{
#[inline]
fn to_usize(&self) -> usize{
self.clone() as usize
}
#[inline]
fn from_usize(v: usize) -> #enum_name{
#(if #is_usize{
#path
}else )* {
panic!("Non existent variant {} in enum {}", v, stringify!(#enum_name))
}
}
}
}
}
fn impl_enum_param(&self) -> proc_macro2::TokenStream {
let enum_name = &self.ident;
let variant_to_enabled = self.variants.iter()
.map(|variant| variant.variant_to_enabled(enum_name));
let variant_to_name = self.variants.iter()
.map(|variant| variant.variant_to_name(enum_name));
let variant_to_add_control = self.variants.iter()
.map(|variant| variant.variant_to_add_control(enum_name));
let len = self.variants.len();
let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
let events_module_name = crate_name("seitan")
.or(crate_name("rin-events"))
.unwrap_or("rin::events".to_owned());
let events_module_name: syn::Path = syn::parse_str(&events_module_name).unwrap();
let gui_module_name = crate_name("rin-gui")
.unwrap_or("rin::gui".to_owned());
let gui_module_name: syn::Path = syn::parse_str(&gui_module_name).unwrap();
quote! {
impl #impl_generics #gui_module_name::EnumParam for #enum_name #ty_generics #where_clause {
fn len() -> usize{
#len
}
fn iter() -> #gui_module_name::enum_param::Iter<Self>{
#gui_module_name::enum_param::Iter::new()
}
fn is_enabled(&self) -> bool{
match self{
#( #variant_to_enabled, )*
}
}
fn name(&self) -> &str{
match self{
#( #variant_to_name, )*
}
}
fn add_control<G: #gui_module_name::Group>(&self,
group: &mut G,
parameter: #events_module_name::Property<'static, bool>,
global_style: Option<#gui_module_name::ToggleStyle>
) -> #events_module_name::StreamRc<'static, bool>
{
match self{
#( #variant_to_add_control, )*
}
}
}
}
}
}
#[proc_macro_derive(EnumParam, attributes(gui))]
pub fn enum_param(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let vis = ast.vis;
let ident = ast.ident;
let generics = ast.generics;
let data = ast.data;
let attrs = ast.attrs.into_iter().filter(|attr| attr.path.is_ident("gui")).collect::<Vec<_>>();
let input = if let syn::Data::Enum(data_enum) = data {
let variants = data_enum.variants;
quote!(#( #attrs )* #vis enum #ident #generics{ #variants }).into()
}else{
panic!("EnumParam can only be derived for enums")
};
let parameter_group = syn::parse_macro_input!(input as EnumParam);
let ret: proc_macro2::TokenStream = parameter_group.impl_enum_param().into_iter()
.chain(parameter_group.impl_clike().into_iter())
.collect();
ret.into()
}