auto_impl 0.1.1

Automatically implement traits for common smart pointers and closures
Documentation
use std::fmt;
use syn;
use quote::{Tokens, ToTokens};

pub struct Trait {
    ident: &'static str,
    tokens: Tokens,
}

impl Trait {
    pub fn new(ident: &'static str, tokens: Tokens) -> Self {
        Trait {
            ident: ident,
            tokens: tokens
        }
    }
}

impl fmt::Display for Trait {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.ident)
    }
}

impl ToTokens for Trait {
    fn to_tokens(&self, tokens: &mut Tokens) {
        tokens.append(&self.tokens);
    }
}

#[derive(Clone)]
struct Item {
    ident: syn::Ident, 
    attrs: Vec<syn::Attribute>,
}

pub struct AutoImpl {
    pub ident: syn::Ident,
    generics: syn::Generics,
    pub associated_types: Vec<AutoImplAssociatedType>,
    pub methods: Vec<AutoImplMethod>,
}

pub enum SelfArg {
    Ref(Option<syn::Lifetime>, syn::Mutability),
    Value(syn::Mutability),
}

pub struct AutoImplMethod {
    pub ident: syn::Ident,
    pub arg_self: Option<SelfArg>,
    pub arg_pats: Vec<syn::Pat>,
    pub arg_tys: Vec<syn::Ty>,
    pub output: Option<syn::Ty>,
    item_impl_factory: Box<Fn(syn::Block) -> Result<syn::TraitItem, String>>,
}

pub struct AutoImplAssociatedType {
    pub ident: syn::Ident,
    item_impl_factory: Box<Fn(syn::Ty) -> Result<syn::TraitItem, String>>,
}

impl AutoImpl {
    pub fn try_parse(item: syn::Item) -> Result<Self, String> {
        match item.node {
            syn::ItemKind::Trait(_, generics, _, items) => {
                let mut methods = Vec::new();
                let mut associated_types = Vec::new();

                for item in items {
                    let node = item.node;

                    let item = Item {
                        ident: item.ident,
                        attrs: item.attrs
                    };

                    match node {
                        syn::TraitItemKind::Method(sig, _) => {
                            let method = AutoImplMethod::try_parse(item, sig)?;
                            methods.push(method);
                        },
                        syn::TraitItemKind::Type(_, _) => {
                            let ty = AutoImplAssociatedType::try_parse(item)?;
                            associated_types.push(ty);
                        },
                        _ => Err("only methods and associated types are supported")?
                    }
                }

                Ok(AutoImpl {
                    ident: item.ident,
                    generics: generics,
                    associated_types: associated_types,
                    methods: methods
                })
            },
            _ => Err("expected a `trait`")?
        }
    }

    pub fn split_generics<'a>(&'a self) -> (syn::TyGenerics<'a>, &[syn::LifetimeDef], &[syn::TyParam], &[syn::WherePredicate]) {
        let (_, trait_tys, _) = self.generics.split_for_impl();
        let impl_lifetimes = &self.generics.lifetimes;
        let impl_tys = &self.generics.ty_params;
        let where_clauses = &self.generics.where_clause.predicates;

        (trait_tys, impl_lifetimes, impl_tys, where_clauses)
    }
}

impl AutoImplMethod {
    fn try_parse(item: Item, sig: syn::MethodSig) -> Result<Self, String> {
        let ident = item.ident.clone();
        let mut arg_self = None;
        let mut arg_pats = Vec::new();
        let mut arg_tys = Vec::new();

        for arg in &sig.decl.inputs {
            match *arg {
                syn::FnArg::SelfRef(ref lifetimes, ref mutability) => {
                    arg_self = Some(SelfArg::Ref(lifetimes.clone(), mutability.clone()));   
                },
                syn::FnArg::SelfValue(ref mutability) => {
                    arg_self = Some(SelfArg::Value(mutability.clone()));
                }
                syn::FnArg::Captured(ref pat, ref ty) => {
                    arg_pats.push(pat.clone());
                    arg_tys.push(ty.clone());
                },
                _ => Err("expected self or ident arg")?
            }
        }

        let output = match sig.decl.output {
            syn::FunctionRetTy::Ty(ref ty) => Some(ty.clone()),
            _ => None
        };

        let item_impl_factory = move |block| {
            let sig = sig.clone();
            let item = item.clone();

            let node = syn::TraitItemKind::Method(sig, Some(block));

            Ok(syn::TraitItem {
                ident: item.ident,
                attrs: vec![],
                node: node,
            })
        };

        Ok(AutoImplMethod {
            ident: ident,
            item_impl_factory: Box::new(item_impl_factory),
            arg_pats: arg_pats, 
            arg_tys: arg_tys,
            arg_self: arg_self,
            output: output
        })
    }

    pub fn build_impl_item<F>(&self, block_factory: F) -> Result<syn::TraitItem, String>
        where F: Fn(&Self) -> Tokens
    {
        let block_tokens = block_factory(&self);

        let block_expr = syn::parse_expr(&block_tokens.to_string())?;

        let block = match block_expr.node {
            syn::ExprKind::Block(_, block) => block,
            _ => Err("expected a block")?
        };

        (self.item_impl_factory)(block)
    }

    pub fn anonymous_arg_lifetimes(&self) -> Vec<syn::Ty> {
        self.arg_tys.iter()
            .map(|ty| {
                match *ty {
                    syn::Ty::Rptr(ref lifetime, ref ty) => {
                        let ty = ty.clone();

                        let lifetime = match lifetime {
                            &Some(ref l) if l.ident.as_ref() == "'static" => lifetime.to_owned(),
                            _ => None
                        };
                        
                        syn::Ty::Rptr(lifetime, ty)
                    },
                    ref ty @ _ => ty.clone()
                }
            })
            .collect()
    }
}

impl AutoImplAssociatedType {
    fn try_parse(item: Item) -> Result<Self, String> {
        let ident = item.ident.clone();
        let item_impl_factory = move |ty| {
            let item = item.clone();

            let node = syn::TraitItemKind::Type(vec![], Some(ty));

            Ok(syn::TraitItem {
                ident: item.ident,
                attrs: vec![],
                node: node,
            })
        };

        Ok(AutoImplAssociatedType {
            ident: ident,
            item_impl_factory: Box::new(item_impl_factory),
        })
    }

    pub fn build_impl_item<F>(&self, ty_factory: F) -> Result<syn::TraitItem, String>
        where F: Fn(&Self) -> Tokens
    {
        let ty_tokens = ty_factory(&self);

        let ty = syn::parse_type(&ty_tokens.to_string())?;

        (self.item_impl_factory)(ty)
    }
}