moq_derive 0.4.1

Mock generator (macro implementations)
Documentation
use std::ops::Not;

use syn::{visit, visit::Visit, FnArg, ItemTrait, Signature, Type};

pub fn validate(trait_def: &ItemTrait) -> Result<(), syn::Error> {
    check_impl_trait(trait_def)?;
    check_anon_lifetime(trait_def)?;
    check_static_func(trait_def)?;
    Ok(())
}

fn check_impl_trait(trait_def: &ItemTrait) -> Result<(), syn::Error> {
    let mut vis = ImplTraitVisitor::default();
    vis.visit_item_trait(trait_def);
    vis.result
}

fn check_anon_lifetime(trait_def: &ItemTrait) -> Result<(), syn::Error> {
    let mut vis = AnonLifetimeVisitor::default();
    vis.visit_item_trait(trait_def);
    vis.result
}

fn check_static_func(trait_def: &ItemTrait) -> Result<(), syn::Error> {
    let mut vis = StaticFuncVisitor::default();
    vis.visit_item_trait(trait_def);
    vis.result
}

struct ImplTraitVisitor {
    result: Result<(), syn::Error>,
}

impl Default for ImplTraitVisitor {
    fn default() -> Self {
        Self { result: Ok(()) }
    }
}

impl Visit<'_> for ImplTraitVisitor {
    fn visit_fn_arg(&mut self, i: &FnArg) {
        match i {
            FnArg::Receiver(i) => {
                visit::visit_receiver(self, i);
            }
            FnArg::Typed(i) => match &*i.ty {
                Type::ImplTrait(_) if self.result.is_ok() => {
                    self.result = Err(syn::Error::new_spanned(
                        i,
                        "`impl Trait` in argument position not supported, use generics `T: Trait` instead",
                    ));
                }
                _ => visit::visit_pat_type(self, i),
            },
        }
    }
}

struct AnonLifetimeVisitor {
    result: Result<(), syn::Error>,
}

impl Default for AnonLifetimeVisitor {
    fn default() -> Self {
        Self { result: Ok(()) }
    }
}

impl Visit<'_> for AnonLifetimeVisitor {
    fn visit_lifetime(&mut self, i: &syn::Lifetime) {
        if i.ident == "_" && self.result.is_ok() {
            self.result = Err(syn::Error::new_spanned(
                i,
                "anonymized lifetimes not supported",
            ))
        }
    }
}

struct StaticFuncVisitor {
    result: Result<(), syn::Error>,
}

impl Default for StaticFuncVisitor {
    fn default() -> Self {
        Self { result: Ok(()) }
    }
}

impl Visit<'_> for StaticFuncVisitor {
    fn visit_signature(&mut self, i: &Signature) {
        if self.result.is_ok() {
            let is_static_func = i
                .inputs
                .iter()
                .any(|item| matches!(item, &FnArg::Receiver(_)))
                .not();

            if is_static_func {
                self.result = Err(syn::Error::new_spanned(i, "static functions not supported"));
            }
        }

        visit::visit_signature(self, i);
    }
}