vectorize_struct 0.1.3

vectorize_struct adds an procedural macro attribute that makes it possible to iterate over Trait Objects of every field of a Struct that implements a specific trait.
//! vectorize_struct adds an procedural macro attribute that makes it possible to iterate over Trait Objects of every field of a Struct that implements a specific trait.
//!
//! # Example
//! ``` rust ignore
//! #![feature(specialization)]
//!
//! //extern crate vectorize_struct;
//! use vectorize_struct::vectorize_struct;
//!
//! use std::fmt::{Debug, Display};
//!
//! #[vectorize_struct(std::fmt::Display, Debug)]
//! struct Struct {
//!     i: i32,
//!     u: u32,
//!     b: bool,
//!     s: String,
//!     buui: (bool, u32, u8, i16),
//!     s2: String,
//!     nd: NoDisplay,
//! }
//!
//! struct NoDisplay {}
//!
//! fn main() {
//!     use vectorized_Struct::*;
//!
//!     let s = Struct {
//!         i: -12,
//!         u: 61,
//!         b: false,
//!         s: String::from("asd"),
//!         buui: (true, 1, 5, -2),
//!         s2: String::from("fgh"),
//!         nd: NoDisplay {},
//!     };
//!
//!     for (name, field) in s.vectorize() as Vec<(Fields, Option<&Display>)> {
//!         if let Some(field) = field {
//!             println!("{:?} can display: {}", name, field);
//!         }
//!     }
//!
//!     for (name, field) in s.vectorize() as Vec<(Fields, Option<&Debug>)> {
//!         if let Some(field) = field {
//!             println!("{:?} can debug: {:?}", name, field);
//!         }
//!     }
//! }
//! ```
#![cfg_attr(docs_rs_workaround, feature(proc_macro))]

extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;

use proc_macro2::{Ident, Span};
use std::collections::HashSet;
use syn::{
    parse::{Parse, ParseStream, Result},
    punctuated::Punctuated,
    ItemStruct,
};

struct Args {
    types: HashSet<syn::Path>,
}

impl Parse for Args {
    fn parse(input: ParseStream) -> Result<Self> {
        let types = Punctuated::<syn::Path, Token![,]>::parse_terminated(input)?;
        Ok(Args {
            types: types.into_iter().collect(),
        })
    }
}
/// Attribute to allow iterating over Trait Objects of every field of a Struct that implements a specific trait.
///
/// # Example
/// ```  rust ignore
/// struct NoDisplay{}
///
/// #[vectorize_struct(std::fmt::Display)]
/// struct ExampleStruct {
///    i : i32,
///    nd : NoDisplay
/// }
///
/// fn doc_example() {
///     use vectorized_ExampleStruct::{Vectorize,Fields};
///
///     let es = ExampleStruct{
///         nd: NoDisplay{},
///         i: 6792,
///     };
///
///     for (name, field) in es.vectorize() as Vec<(Fields, Option<&Display>)> {
///         if let Some(field) = field {
///             println!("{:?} can display: {}", name, field);
///         }
///     }
/// }
/// ```
#[proc_macro_attribute]
pub fn vectorize_struct(types: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let args: Args = syn::parse(types).unwrap();
    let strct: ItemStruct = syn::parse(input).unwrap();

    let mut mod_name = String::from("vectorized_");
    mod_name.push_str(&strct.ident.to_string());
    let mod_ident = Ident::new(&mod_name, Span::call_site());

    let struct_ident = &strct.ident;

    let enum_members: &Vec<_> = &strct
        .fields
        .iter()
        .map(|f| {
            let ident = f.ident.as_ref().unwrap();
            quote!(#ident)
        }).collect();

    let try_gets: &Vec<_> = &args
        .types
        .iter()
        .map(|t| {
            quote!(
            impl<'a, T : #t> TryGet<'a, &'a #t> for T {
                fn get(&'a self) -> Option<&'a #t>{
                    Some(self)
                }
            }

            impl<'a, T> TryGet<'a, &'a #t> for T {
                default fn get(&'a self) -> Option<&'a #t>{
                    None
                }
            }
        )
        }).collect();

    let vector_members: &Vec<_> = &strct
        .fields
        .iter()
        .map(|f| {
            let ident = f.ident.as_ref().unwrap();
            quote!((Fields::#ident,self.#ident.get()))
        }).collect();

    let vectorizes = args.types.iter().map(move |t| {
        quote!(
            impl<'a> Vectorize<'a, &'a #t> for #struct_ident {
                fn vectorize(&'a self) -> Vec<(Fields,Option<&'a #t>)>{
                    vec![#(#vector_members,)*]
                }
            }
        )
    });

    let result = quote!(
        #strct

        mod #mod_ident {
            use super::*;

            pub trait TryGet<'a, G> {
               fn get(&'a self) -> Option<G>;
            }

            #(#try_gets )*

            pub trait Vectorize<'a, G> {
                fn vectorize(&'a self) -> Vec<(Fields, Option<G>)>;
            }

            #(#vectorizes )*

            #[allow(bad_style)]
            #[derive(Debug)]
            pub enum Fields {
                #(#enum_members ,)*
            }
        }

    ).into();
    //panic!("{}",result);
    result
}