proc-macro-assertions 0.1.5

Easily create asserts on proc macro inputs
Documentation
use better_any::TidAble;

use crate::{
    assert::Assert, assertable::Assertable, generatable::Generatable, into_template::TypeEq,
    maybe_borrowed::MaybeBorrowed, raw_assert::RawAssert,
};

/// A type representing some `T` with some generics attached to it.
/// Usually created by [`AttachGenerics::with_generics`].
pub struct WithGenerics<'a, T> {
    /// The data the generics are attached to
    pub data: T,
    /// The generics that are attached to the data.
    pub generics: Option<MaybeBorrowed<'a, syn::Generics>>,
}

/// A trait meant to be implemented for all types that may want some
/// generics to be attached to them.
pub trait AttachGenerics<'a> {
    /// An optional type wrapper around the type the generics will be attached to
    /// Used by this crate to wrap types with [`MaybeBorrowed`] to allow both
    /// references and owned types to be represented by the same type.
    type Output;

    /// Attach some generics `G` to `Self`. This allows for the generics to
    /// to not exactly be [`syn::Generics`] but any type that can be
    /// turned into `MaybeBorrowed<syn::Generics>`. This allows for
    /// both `&syn::Generics` and `syn::Generics` to be attached using the
    /// same trait.
    fn with_generics<G>(self, generics: G) -> WithGenerics<'a, Self::Output>
    where
        G: Into<MaybeBorrowed<'a, syn::Generics>>,
        Self: Sized,
    {
        self.with_optional_generics(Some(generics))
    }

    fn with_optional_generics<G>(self, generics: Option<G>) -> WithGenerics<'a, Self::Output>
    where
        G: Into<MaybeBorrowed<'a, syn::Generics>>;
}

/// A trait meant to be implemented for all types that may want some
/// generics to be attached to them.
pub trait AttachGenericsWithAssert<'a, A> {
    /// An optional type wrapper around the type the generics will be attached to
    /// Used by this crate to wrap types with [`MaybeBorrowed`] to allow both
    /// references and owned types to be represented by the same type.
    type Output;

    /// Attach some generics `G` to `Self`. This allows for the generics to
    /// to not exactly be [`syn::Generics`] but any type that can be
    /// turned into `MaybeBorrowed<syn::Generics>`. This allows for
    /// both `&syn::Generics` and `syn::Generics` to be attached using the
    /// same trait.
    fn with_generics<B>(
        self,
        generics: impl Into<MaybeBorrowed<'a, syn::Generics>>,
    ) -> WithGenerics<'a, Self::Output>
    where
        B: TypeEq<This = A>,
        Self: Sized,
    {
        self.with_optional_generics::<B>(Some(generics))
    }

    fn with_optional_generics<B>(
        self,
        generics: Option<impl Into<MaybeBorrowed<'a, syn::Generics>>>,
    ) -> WithGenerics<'a, Self::Output>
    where
        B: TypeEq<This = A>;
}

impl<'a, Gen, A> AttachGenericsWithAssert<'a, A> for Gen
where
    Gen: Generatable<'a, A>,
    A: 'a + TidAble<'a>,
{
    type Output = MaybeBorrowed<'a, Self>;

    fn with_optional_generics<B>(
        self,
        generics: Option<impl Into<MaybeBorrowed<'a, syn::Generics>>>,
    ) -> WithGenerics<'a, Self::Output>
    where
        B: TypeEq<This = A>,
    {
        WithGenerics {
            data: self.into(),
            generics: generics.map(Into::into),
        }
    }
}

impl<'a, Gen> AttachGenerics<'a> for MaybeBorrowed<'a, Gen> {
    type Output = MaybeBorrowed<'a, Gen>;

    fn with_optional_generics<G>(self, generics: Option<G>) -> WithGenerics<'a, Self::Output>
    where
        G: Into<MaybeBorrowed<'a, syn::Generics>>,
    {
        WithGenerics {
            data: self,
            generics: generics.map(Into::into),
        }
    }
}

impl<'a, Gen, A> AttachGenerics<'a> for Assert<'a, Gen, A>
where
    Gen: Generatable<'a, A>,
    A: 'a + TidAble<'a>,
{
    type Output = Assert<'a, Gen, A>;

    fn with_optional_generics<G>(self, generics: Option<G>) -> WithGenerics<'a, Self::Output>
    where
        G: Into<MaybeBorrowed<'a, syn::Generics>>,
    {
        WithGenerics {
            data: self,
            generics: generics.map(Into::into),
        }
    }
}

impl<'a, G, A> Assertable<'a> for WithGenerics<'a, Assert<'a, G, A>>
where
    G: Generatable<'a, A> + Eq + Ord,
    A: Eq + Ord + TidAble<'a>,
{
    type Output = RawAssert<'a, G, A>;

    fn do_assert(self) -> RawAssert<'a, G, A> {
        RawAssert {
            template: self.data.template,
            generics: check_generics(self.generics),
            assert: self.data.assert,
        }
    }
}

fn check_generics(
    generics: Option<MaybeBorrowed<syn::Generics>>,
) -> Option<MaybeBorrowed<syn::Generics>> {
    generics.and_then(|f| {
        if f.lt_token.is_some()
            && f.gt_token.is_some()
            && !f.params.is_empty()
            && f.where_clause
                .as_ref()
                .is_some_and(|w| !w.predicates.is_empty())
        {
            Some(f)
        } else {
            None
        }
    })
}