reflect 0.0.15

The "but I thought Rust doesn't have reflection?" memorial brand new way of defining procedural macros.
Documentation
use crate::{Ident, Path, Type, TypeNode};
use syn::{BoundLifetimes, PredicateLifetime, WhereClause, WherePredicate};

#[derive(Debug, Clone)]
pub struct Generics {
    /// Represents the generic params without bounds.
    /// The bounds are moved to constraints.
    pub(crate) params: Vec<GenericParam>,

    /// Essentially represents the where clause
    pub(crate) constraints: Vec<GenericConstraint>,
}

#[derive(Debug, Clone)]
pub(crate) enum GenericParam {
    Type(TypeParam),
    Lifetime(Lifetime),
    Const(ConstParam),
}

#[derive(Debug, Clone)]
pub(crate) struct TypeParam {
    pub(crate) ident: Ident,
}

#[derive(Debug, Clone)]
pub(crate) enum GenericConstraint {
    Type(PredicateType),
    Lifetime(LifetimeParam),
}

#[derive(Debug, Clone)]
pub(crate) struct PredicateType {
    /// A set of bound Lifetimes: `for<'a, 'b, 'c>`.
    pub(crate) lifetimes: Vec<Lifetime>,
    pub(crate) bounded_ty: Type,
    pub(crate) bounds: Vec<TypeParamBound>,
}

#[derive(Debug, Clone)]
pub(crate) enum TypeParamBound {
    Trait(TraitBound),
    Lifetime(Lifetime),
}

#[derive(Debug, Clone)]
pub(crate) struct TraitBound {
    /// A set of bound Lifetimes: `for<'a, 'b, 'c>`.
    pub(crate) lifetimes: Vec<Lifetime>,
    pub(crate) path: Path,
}

#[derive(Debug, Clone)]
pub(crate) struct Lifetime {
    pub(crate) ident: Ident,
}

#[derive(Debug, Clone)]
pub(crate) struct LifetimeParam {
    pub(crate) ident: Ident,
    pub(crate) bounds: Vec<Lifetime>,
}

#[derive(Debug, Clone)]
pub(crate) struct ConstParam {
    pub(crate) private: (),
}

#[derive(Debug, Clone)]
pub struct GenericArguments {
    pub(crate) args: Vec<GenericArgument>,
}

#[derive(Debug, Clone)]
pub(crate) enum GenericArgument {
    Type(Type),
    Lifetime(Lifetime),
    AssocType(AssocType),
    Constraint(Constraint),
    Const(Expr),
}

#[derive(Debug, Clone)]
pub(crate) struct AssocType {
    pub(crate) ident: Ident,
    pub(crate) ty: Type,
}

#[derive(Debug, Clone)]
pub(crate) struct Constraint {
    pub(crate) ident: Ident,
    pub(crate) bounds: Vec<TypeParamBound>,
}

#[derive(Debug, Clone)]
pub(crate) struct Expr {
    pub(crate) private: (),
}

impl Generics {
    pub(crate) fn syn_to_generics(generics: syn::Generics) -> Self {
        let (params, mut constraints) = syn_to_generic_params(generics.params);
        if let Some(where_clause) = generics.where_clause {
            constraints.extend(syn_to_generic_constraints(where_clause));
        }
        Generics {
            params,
            constraints,
        }
    }
}

fn syn_to_bound_lifetimes(lifetimes: Option<BoundLifetimes>) -> Vec<Lifetime> {
    lifetimes.map_or_else(Vec::new, |lifetimes| {
        lifetimes
            .lifetimes
            .into_iter()
            .filter_map(|generic_param| match generic_param {
                syn::GenericParam::Lifetime(syn::LifetimeParam {
                    lifetime: syn::Lifetime { ident, .. },
                    ..
                }) => Some(Lifetime {
                    ident: Ident::from(ident),
                }),
                _ => None,
            })
            .collect()
    })
}

fn syn_to_generic_constraints(
    where_clause: WhereClause,
) -> impl Iterator<Item = GenericConstraint> {
    where_clause
        .predicates
        .into_iter()
        .map(|predicate| match predicate {
            WherePredicate::Type(syn::PredicateType {
                lifetimes,
                bounded_ty,
                bounds,
                ..
            }) => GenericConstraint::Type(PredicateType {
                lifetimes: syn_to_bound_lifetimes(lifetimes),
                bounded_ty: Type::syn_to_type(bounded_ty),
                bounds: syn_to_type_param_bounds(bounds),
            }),
            WherePredicate::Lifetime(PredicateLifetime {
                lifetime: syn::Lifetime { ident, .. },
                bounds,
                ..
            }) => GenericConstraint::Lifetime(LifetimeParam {
                ident: Ident::from(ident),
                bounds: bounds
                    .into_iter()
                    .map(|syn::Lifetime { ident, .. }| Lifetime {
                        ident: Ident::from(ident),
                    })
                    .collect(),
            }),
            _ => unimplemented!(),
        })
}

fn syn_to_generic_params<T>(params: T) -> (Vec<GenericParam>, Vec<GenericConstraint>)
where
    T: IntoIterator<Item = syn::GenericParam>,
{
    let mut constraints = Vec::new();
    let params = params
        .into_iter()
        .map(|param| match param {
            syn::GenericParam::Type(syn::TypeParam { ident, bounds, .. }) => {
                let ident = Ident::from(ident);
                if !bounds.is_empty() {
                    constraints.push(GenericConstraint::Type(PredicateType {
                        lifetimes: Vec::new(),
                        bounded_ty: Type(TypeNode::Path(Path::ident_to_path(ident.clone()))),
                        bounds: syn_to_type_param_bounds(bounds),
                    }));
                }

                GenericParam::Type(TypeParam { ident })
            }
            syn::GenericParam::Lifetime(syn::LifetimeParam {
                lifetime: syn::Lifetime { ident, .. },
                bounds,
                ..
            }) => {
                let ident = Ident::from(ident);
                if !bounds.is_empty() {
                    constraints.push(GenericConstraint::Lifetime(LifetimeParam {
                        ident: ident.clone(),
                        bounds: bounds
                            .into_iter()
                            .map(|syn::Lifetime { ident, .. }| Lifetime {
                                ident: Ident::from(ident),
                            })
                            .collect(),
                    }));
                }
                GenericParam::Lifetime(Lifetime { ident })
            }
            syn::GenericParam::Const(_const) => unimplemented!("Generics::syn_to_generics: Const"),
        })
        .collect();
    (params, constraints)
}

pub(crate) fn syn_to_type_param_bounds<T>(bounds: T) -> Vec<TypeParamBound>
where
    T: IntoIterator<Item = syn::TypeParamBound>,
{
    bounds
        .into_iter()
        .map(|type_param_bound| match type_param_bound {
            syn::TypeParamBound::Trait(syn::TraitBound {
                lifetimes, path, ..
            }) => TypeParamBound::Trait(TraitBound {
                lifetimes: syn_to_bound_lifetimes(lifetimes),
                path: Path::syn_to_path(path),
            }),
            syn::TypeParamBound::Lifetime(lifetime) => TypeParamBound::Lifetime(Lifetime {
                ident: Ident::from(lifetime.ident),
            }),
            _ => unimplemented!(),
        })
        .collect()
}

impl GenericArgument {
    pub(crate) fn syn_to_generic_argument(arg: syn::GenericArgument) -> Self {
        match arg {
            syn::GenericArgument::Type(ty) => GenericArgument::Type(Type::syn_to_type(ty)),

            syn::GenericArgument::Lifetime(lifetime) => GenericArgument::Lifetime(Lifetime {
                ident: Ident::from(lifetime.ident),
            }),

            syn::GenericArgument::AssocType(binding) => GenericArgument::AssocType(AssocType {
                ident: Ident::from(binding.ident),
                ty: Type::syn_to_type(binding.ty),
            }),

            syn::GenericArgument::Constraint(constraint) => {
                GenericArgument::Constraint(Constraint {
                    ident: Ident::from(constraint.ident),
                    bounds: syn_to_type_param_bounds(constraint.bounds),
                })
            }

            syn::GenericArgument::Const(_expr) => {
                unimplemented!("GenericArguments::syn_to_generic_arguments: Const")
            }

            _ => unimplemented!(),
        }
    }
}