devise_core 0.1.0

A library for devising derives and other procedural macros.
use syn::{*, punctuated::Punctuated, token::Comma};

pub trait PathExt {
    fn is(&self, global: bool, segments: &[&str]) -> bool;
    fn is_local(&self, segments: &[&str]) -> bool;
    fn is_global(&self, segments: &[&str]) -> bool;
    fn generics(&self) -> Option<&Punctuated<GenericArgument, Comma>>;
}

pub trait TypeExt {
    fn strip_lifetimes(&mut self);
    fn with_stripped_lifetimes(&self) -> Type;
}

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum GenericKind { Lifetime, Type, Binding, Const, Constraint }

pub trait GenericExt {
    fn kind(&self) -> GenericKind;
}

pub trait Split2<A, B>: Sized + Iterator {
    fn split2(self) -> (Vec<A>, Vec<B>);
}

pub trait Split3<A, B, C>: Sized + Iterator {
    fn split3(self) -> (Vec<A>, Vec<B>, Vec<C>);
}

impl PathExt for Path {
    fn is(&self, global: bool, segments: &[&str]) -> bool {
        if self.leading_colon.is_some() != global || self.segments.len() != segments.len() {
            return false;
        }

        for (segment, wanted) in self.segments.iter().zip(segments.iter()) {
            if segment.ident != wanted {
                return false;
            }
        }

        true
    }

    fn is_local(&self, segments: &[&str]) -> bool {
        self.is(false, segments)
    }

    fn is_global(&self, segments: &[&str]) -> bool {
        self.is(true, segments)
    }

    fn generics(&self) -> Option<&Punctuated<GenericArgument, Comma>> {
        self.segments.last().and_then(|last| {
            match last.value().arguments {
                PathArguments::AngleBracketed(ref args) => Some(&args.args),
                _ => None
            }
        })
    }
}

impl<A, B, I: IntoIterator<Item = (A, B)> + Iterator> Split2<A, B> for I {
    fn split2(self) -> (Vec<A>, Vec<B>) {
        let (mut first, mut second) = (vec![], vec![]);
        self.into_iter().for_each(|(a, b)| {
            first.push(a);
            second.push(b);
        });

        (first, second)
    }
}

impl<A, B, C, I: IntoIterator<Item = (A, B, C)> + Iterator> Split3<A, B, C> for I {
    fn split3(self) -> (Vec<A>, Vec<B>, Vec<C>) {
        let (mut first, mut second, mut third) = (vec![], vec![], vec![]);
        self.into_iter().for_each(|(a, b, c)| {
            first.push(a);
            second.push(b);
            third.push(c);
        });

        (first, second, third)
    }
}

impl TypeExt for Type {
    fn strip_lifetimes(&mut self) {
        strip(self);
    }

    fn with_stripped_lifetimes(&self) -> Type {
        let mut new = self.clone();
        new.strip_lifetimes();
        new
    }
}

fn make_wild(lifetime: &mut Lifetime) {
    lifetime.ident = Ident::new("_", lifetime.ident.span());
}

fn strip(ty: &mut Type) {
    match *ty {
        Type::Reference(ref mut inner) => {
            inner.lifetime.as_mut().map(make_wild);
            strip(&mut inner.elem);
        }
        Type::Slice(ref mut inner) => strip(&mut inner.elem),
        Type::Array(ref mut inner) => strip(&mut inner.elem),
        Type::Ptr(ref mut inner) => strip(&mut inner.elem),
        Type::Paren(ref mut inner) => strip(&mut inner.elem),
        Type::Group(ref mut inner) => strip(&mut inner.elem),
        Type::BareFn(ref mut inner) => {
            inner.lifetimes.as_mut().map(strip_bound_lifetimes);
            if let ReturnType::Type(_, ref mut ty) = inner.output {
                strip(ty);
            }

            inner.inputs.iter_mut().for_each(|input| strip(&mut input.ty));
        }
        Type::Tuple(ref mut inner) => {
            inner.elems.iter_mut().for_each(strip);
        }
        Type::Path(ref mut inner) => {
            if let Some(ref mut qself) = inner.qself {
                strip(&mut qself.ty);
            }

            strip_path(&mut inner.path);
        }
        Type::ImplTrait(ref mut inner) => strip_bounds(&mut inner.bounds),
        Type::TraitObject(ref mut inner) => strip_bounds(&mut inner.bounds),
        Type::Infer(_) | Type::Macro(_) | Type::Verbatim(_) | Type::Never(_) => {  }
    }
}

fn strip_bound_lifetimes(bound: &mut BoundLifetimes) {
    bound.lifetimes.iter_mut().for_each(|d| make_wild(&mut d.lifetime));
}

fn strip_path(path: &mut Path) {
    for segment in path.segments.iter_mut() {
        use syn::GenericArgument::*;

        match segment.arguments {
            PathArguments::AngleBracketed(ref mut inner) => {
                let args = inner.args.clone();
                inner.args = args.into_pairs().filter_map(|mut pair| {
                    match pair.value_mut() {
                        Lifetime(ref mut l) => make_wild(l),
                        Type(ref mut ty) => strip(ty),
                        Binding(ref mut inner) => strip(&mut inner.ty),
                        Constraint(ref mut inner) => strip_bounds(&mut inner.bounds),
                        Const(..) => { /* ? */ }
                    }

                    Some(pair)
                }).collect();
            }
            PathArguments::Parenthesized(ref mut args) => {
                args.inputs.iter_mut().for_each(strip);
                if let ReturnType::Type(_, ref mut ty) = args.output {
                    strip(ty);
                }
            }
            PathArguments::None => {  }
        }
    }
}

fn strip_bounds(bounds: &mut Punctuated<TypeParamBound, token::Add>) {
    let old_bounds = bounds.clone();
    *bounds = old_bounds.into_pairs().filter_map(|mut pair| {
        match pair.value_mut() {
            TypeParamBound::Lifetime(ref mut l) => make_wild(l),
            TypeParamBound::Trait(ref mut inner) => {
                inner.lifetimes.as_mut().map(strip_bound_lifetimes);
                strip_path(&mut inner.path);
            }
        }

        Some(pair)
    }).collect();
}

impl GenericExt for GenericArgument {
    fn kind(&self) -> GenericKind {
        match *self {
            GenericArgument::Lifetime(..) => GenericKind::Lifetime,
            GenericArgument::Type(..) => GenericKind::Type,
            GenericArgument::Binding(..) => GenericKind::Binding,
            GenericArgument::Constraint(..) => GenericKind::Constraint,
            GenericArgument::Const(..) => GenericKind::Const,
        }
    }
}

impl GenericExt for GenericParam {
    fn kind(&self) -> GenericKind {
        match *self {
            GenericParam::Lifetime(..) => GenericKind::Lifetime,
            GenericParam::Type(..) => GenericKind::Type,
            GenericParam::Const(..) => GenericKind::Const,
        }
    }
}