object-rainbow-derive 0.0.0-a.21

derive macros for object-rainbow
Documentation
use std::collections::BTreeSet;

use syn::{AngleBracketedGenericArguments, Expr, Ident, Path, Type};

#[derive(Clone, Copy)]
pub struct GContext<'a> {
    pub g: &'a BTreeSet<Ident>,
    pub always: bool,
}

fn expr_contains_generics(cx: GContext, expr: &Expr) -> bool {
    match expr {
        Expr::Path(expr) => path_contains_generics(cx, &expr.path),
        _ => unimplemented!(),
    }
}

fn args_contains_generics(cx: GContext, args: &AngleBracketedGenericArguments) -> bool {
    args.args.iter().any(|arg| match arg {
        syn::GenericArgument::Type(ty) => type_contains_generics(cx, ty),
        syn::GenericArgument::AssocType(ty) => {
            ty.generics
                .as_ref()
                .is_some_and(|args| args_contains_generics(cx, args))
                || type_contains_generics(cx, &ty.ty)
        }
        syn::GenericArgument::Const(expr) => expr_contains_generics(cx, expr),
        syn::GenericArgument::AssocConst(expr) => {
            expr.generics
                .as_ref()
                .is_some_and(|args| args_contains_generics(cx, args))
                || expr_contains_generics(cx, &expr.value)
        }
        _ => false,
    })
}

fn path_contains_generics(cx: GContext, path: &Path) -> bool {
    path.segments.iter().any(|seg| match &seg.arguments {
        syn::PathArguments::None => cx.g.contains(&seg.ident),
        syn::PathArguments::AngleBracketed(args) => args_contains_generics(cx, args),
        syn::PathArguments::Parenthesized(args) => {
            args.inputs.iter().any(|ty| type_contains_generics(cx, ty))
                || match &args.output {
                    syn::ReturnType::Default => false,
                    syn::ReturnType::Type(_, ty) => type_contains_generics(cx, ty),
                }
        }
    })
}

pub fn type_contains_generics(cx: GContext, ty: &Type) -> bool {
    if cx.always {
        return true;
    }
    match ty {
        Type::Array(ty) => type_contains_generics(cx, &ty.elem),
        Type::BareFn(ty) => {
            ty.inputs.iter().any(|a| type_contains_generics(cx, &a.ty))
                || match &ty.output {
                    syn::ReturnType::Default => false,
                    syn::ReturnType::Type(_, ty) => type_contains_generics(cx, ty),
                }
        }
        Type::Group(ty) => type_contains_generics(cx, &ty.elem),
        Type::Macro(_) => false,
        Type::Paren(ty) => type_contains_generics(cx, &ty.elem),
        Type::Path(ty) => path_contains_generics(cx, &ty.path),
        Type::Reference(ty) => type_contains_generics(cx, &ty.elem),
        Type::Slice(ty) => type_contains_generics(cx, &ty.elem),
        Type::Tuple(ty) => ty.elems.iter().any(|ty| type_contains_generics(cx, ty)),
        _ => false,
    }
}