use meta_tools_min::*;
use proc_macro_tools::quote::{ ToTokens, TokenStreamExt };
use proc_macro_tools::syn::parse::*;
use proc_macro_tools::syn::spanned::Spanned;
use proc_macro_tools::*;
use std::collections::HashMap;
use iter_tools::{ process_results };
use convert_case::{Case, Casing};
pub type Result< T > = std::result::Result< T, syn::Error >;
pub struct FnQuick
{
pub attrs : Vec< syn::Attribute >,
pub vis : syn::Visibility,
pub sig : syn::Signature,
pub block : Option< proc_macro2::TokenStream >,
}
impl quote::ToTokens for FnQuick
{
fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream )
{
for attr in self.attrs.iter()
{
attr.to_tokens( tokens );
}
self.sig.to_tokens( tokens );
match &self.block
{
Some( block ) => tokens.append( proc_macro2::Group::new( proc_macro2::Delimiter::Brace, block.to_token_stream() ) ),
None => tokens.append( proc_macro2::Punct::new( ';', proc_macro2::Spacing::Alone ) ),
}
}
}
pub enum Element
{
Fn( FnQuick ),
#[ allow( dead_code ) ]
Signature( FnQuick ),
Field( syn::Field ),
}
impl Parse for Element
{
fn parse( input : ParseStream< '_ > ) -> Result< Self >
{
let attrs : Vec< syn::Attribute > = input.call( syn::Attribute::parse_outer )?;
let vis : syn::Visibility = input.parse()?;
let lookahead1 = input.lookahead1();
if lookahead1.peek( syn::Token!{ fn } )
{
let sig : syn::Signature = input.parse()?;
let lookahead2 = input.lookahead1();
if lookahead2.peek( syn::token::Brace )
{
let input2;
let _brace_token : syn::token::Brace = syn::braced!( input2 in input );
let block : proc_macro2::TokenStream = input2.parse()?;
let fn_desc = FnQuick
{
attrs,
vis,
sig,
block : Some( block ),
};
return Ok( Element::Fn( fn_desc ) );
}
else
{
let fn_desc = FnQuick
{
attrs,
vis,
sig,
block : None,
};
return Ok( Element::Fn( fn_desc ) );
}
}
else
{
input.call( syn::Field::parse_named ).map( | mut e |
{
e.vis = vis;
e.attrs = attrs;
Element::Field( e )
})
}
}
}
#[allow( dead_code )]
struct OptionsDescriptor
{
attrs : Vec< syn::Attribute >,
vis : syn::Visibility,
ident : syn::Ident,
generics: syn::Generics,
brace_token : syn::token::Brace,
methods_map : HashMap< String, FnQuick >,
signatures_map : HashMap< String, FnQuick >,
fields_map : HashMap< String, syn::Field >,
}
impl Parse for OptionsDescriptor
{
fn parse( input : ParseStream< '_ > ) -> Result< Self >
{
let input2;
let vis = input.parse()?;
let ident = input.parse()?;
let generics = input.parse()?;
let brace_token = syn::braced!( input2 in input );
let mut attrs = input2.call( syn::Attribute::parse_inner )?;
let elements : syn::punctuated::Punctuated< Element, syn::Token!{ ; } > = input2.parse_terminated( Element::parse )?;
let mut methods_map = hmap!{};
let mut signatures_map = hmap!{};
let mut fields_map = hmap!{};
for attr in attrs.iter_mut()
{
attr.style = syn::AttrStyle::Outer;
}
for element in elements.into_iter()
{
match element
{
Element::Fn( f ) =>
{
methods_map.insert( f.sig.ident.to_string(), f );
},
Element::Signature( f ) =>
{
signatures_map.insert( f.sig.ident.to_string(), f );
},
Element::Field( f ) =>
{
let key = f.ident.as_ref().ok_or_else( || syn_err!( &f.clone(), "Field does not have name: {}", qt!{ #f } ) )?.to_string();
fields_map.insert( key, f );
},
}
}
let result = OptionsDescriptor
{
vis,
ident,
generics,
brace_token,
attrs,
methods_map,
signatures_map,
fields_map,
};
Ok( result )
}
}
#[ derive( Debug ) ]
pub struct AccessorDescriptor
{
attr : proc_macro2::TokenStream,
signature : proc_macro2::TokenStream,
body : proc_macro2::TokenStream,
}
impl quote::ToTokens for AccessorDescriptor
{
fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream )
{
self.attr.to_tokens( tokens );
self.signature.to_tokens( tokens );
self.body.to_tokens( tokens );
}
}
fn getter_gen( name : &str, field : &syn::Field ) -> Result< AccessorDescriptor >
{
let name_ident = syn::Ident::new( &name, field.span() );
let ty = &field.ty;
let ty_is_ref = matches!( ty, syn::Type::Reference( _ ) );
let ty2 = if ty_is_ref
{
ty.to_token_stream()
}
else
{
qt!{ & #ty }
};
let attr = qt!
{
#[ inline ]
};
let signature = qt!
{
fn #name_ident( &self ) -> #ty2
};
let body = qt!
{
{
&self.#name_ident
}
};
let result = AccessorDescriptor
{
attr,
signature,
body,
};
Ok( result )
}
fn mutter_gen( name : &str, field : &syn::Field ) -> Result< AccessorDescriptor >
{
let name_ident = syn::Ident::new( &name, field.span() );
let name_mut_ident = syn::Ident::new( &format!( "{}_mut", name ), field.span() );
let ty = &field.ty;
let ty2 = qt!{ &mut #ty };
let attr = qt!
{
#[ inline ]
};
let signature = qt!
{
fn #name_mut_ident( &mut self ) -> #ty2
};
let body = qt!
{
{
&mut self.#name_ident
}
};
let result = AccessorDescriptor
{
attr,
signature,
body,
};
Ok( result )
}
fn perform_gen( options_descriptor : &OptionsDescriptor ) -> ( proc_macro2::TokenStream, proc_macro2::TokenStream )
{
let mut perform = qt!{};
let mut attr_perform = qt!{};
if let Some( perform_fn ) = options_descriptor.methods_map.get( "perform" )
{
let sig = &perform_fn.sig;
attr_perform = qt!{ #[ perform( #sig ) ] };
perform = qt!
{
#[ allow( unused_attributes ) ]
#[ inline ]
#perform_fn
}
}
( perform, attr_perform )
}
pub fn options( _attr : proc_macro::TokenStream, item : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream >
{
let options_descriptor = match syn::parse::< OptionsDescriptor >( item )
{
Ok( syntax_tree ) => syntax_tree,
Err( err ) => return Err( err ),
};
let name_ident = &options_descriptor.ident;
let name_options_adapter_str = String::from( name_ident.to_string() ).from_case( Case::Snake ).to_case( Case::UpperCamel );
let name_options_adapter_str = format!( "{}OptionsAdapter", name_options_adapter_str );
let name_options_adapter_ident = syn::Ident::new( &name_options_adapter_str, name_ident.span() );
let generics = &options_descriptor.generics;
let attrs = &options_descriptor.attrs;
let mut fields_define = Vec::< &syn::Field >::new();
for ( _name, field ) in options_descriptor.fields_map.iter()
{
fields_define.push( field );
}
let ( perform, attr_perform ) = perform_gen( &options_descriptor );
let getters = options_descriptor.fields_map.iter().map( | ( key, field ) | getter_gen( key, field ) );
let getters : Vec< _ > = process_results( getters, | iter | iter.collect() )?;
let getters_signatures : Vec< _ > = getters.iter().map( | e | e.signature.clone() ).collect();
let mutters = options_descriptor.fields_map.iter().map( | ( key, field ) | mutter_gen( key, field ) );
let mutters : Vec< _ > = process_results( mutters, | iter | iter.collect() )?;
let mutters_signatures : Vec< _ > = mutters.iter().map( | e | e.signature.clone() ).collect();
let result = qt!
{
pub mod #name_ident
{
use super::Former;
#( #attrs )*
#[ derive( Former, PartialEq, Debug ) ]
#attr_perform
pub struct Options #generics
{
#( #fields_define, )*
}
pub trait OptionsAdapter #generics
{
#( #getters_signatures ; )*
#( #mutters_signatures ; )*
#perform
}
impl #generics OptionsAdapter #generics for Options #generics
{
#( #getters )*
#( #mutters )*
}
#[ inline ]
pub fn former #generics() -> OptionsFormer #generics
{
Options::#generics::former()
}
pub mod prelude
{
pub use super::OptionsAdapter as #name_options_adapter_ident;
}
}
#[ inline ]
pub fn #name_ident #generics () -> #name_ident::OptionsFormer #generics
{
#name_ident::former::#generics()
}
};
Ok( result )
}