proc_macro_assertions/
add_generics.rs

1use better_any::TidAble;
2
3use crate::{
4    assert::Assert, assertable::Assertable, generatable::Generatable, into_template::TypeEq,
5    maybe_borrowed::MaybeBorrowed, raw_assert::RawAssert,
6};
7
8/// A type representing some `T` with some generics attached to it.
9/// Usually created by [`AttachGenerics::with_generics`].
10pub struct WithGenerics<'a, T> {
11    /// The data the generics are attached to
12    pub data: T,
13    /// The generics that are attached to the data.
14    pub generics: Option<MaybeBorrowed<'a, syn::Generics>>,
15}
16
17/// A trait meant to be implemented for all types that may want some
18/// generics to be attached to them.
19pub trait AttachGenerics<'a> {
20    /// An optional type wrapper around the type the generics will be attached to
21    /// Used by this crate to wrap types with [`MaybeBorrowed`] to allow both
22    /// references and owned types to be represented by the same type.
23    type Output;
24
25    /// Attach some generics `G` to `Self`. This allows for the generics to
26    /// to not exactly be [`syn::Generics`] but any type that can be
27    /// turned into `MaybeBorrowed<syn::Generics>`. This allows for
28    /// both `&syn::Generics` and `syn::Generics` to be attached using the
29    /// same trait.
30    fn with_generics<G>(self, generics: G) -> WithGenerics<'a, Self::Output>
31    where
32        G: Into<MaybeBorrowed<'a, syn::Generics>>,
33        Self: Sized,
34    {
35        self.with_optional_generics(Some(generics))
36    }
37
38    fn with_optional_generics<G>(self, generics: Option<G>) -> WithGenerics<'a, Self::Output>
39    where
40        G: Into<MaybeBorrowed<'a, syn::Generics>>;
41}
42
43/// A trait meant to be implemented for all types that may want some
44/// generics to be attached to them.
45pub trait AttachGenericsWithAssert<'a, A> {
46    /// An optional type wrapper around the type the generics will be attached to
47    /// Used by this crate to wrap types with [`MaybeBorrowed`] to allow both
48    /// references and owned types to be represented by the same type.
49    type Output;
50
51    /// Attach some generics `G` to `Self`. This allows for the generics to
52    /// to not exactly be [`syn::Generics`] but any type that can be
53    /// turned into `MaybeBorrowed<syn::Generics>`. This allows for
54    /// both `&syn::Generics` and `syn::Generics` to be attached using the
55    /// same trait.
56    fn with_generics<B>(
57        self,
58        generics: impl Into<MaybeBorrowed<'a, syn::Generics>>,
59    ) -> WithGenerics<'a, Self::Output>
60    where
61        B: TypeEq<This = A>,
62        Self: Sized,
63    {
64        self.with_optional_generics::<B>(Some(generics))
65    }
66
67    fn with_optional_generics<B>(
68        self,
69        generics: Option<impl Into<MaybeBorrowed<'a, syn::Generics>>>,
70    ) -> WithGenerics<'a, Self::Output>
71    where
72        B: TypeEq<This = A>;
73}
74
75impl<'a, Gen, A> AttachGenericsWithAssert<'a, A> for Gen
76where
77    Gen: Generatable<'a, A>,
78    A: 'a + TidAble<'a>,
79{
80    type Output = MaybeBorrowed<'a, Self>;
81
82    fn with_optional_generics<B>(
83        self,
84        generics: Option<impl Into<MaybeBorrowed<'a, syn::Generics>>>,
85    ) -> WithGenerics<'a, Self::Output>
86    where
87        B: TypeEq<This = A>,
88    {
89        WithGenerics {
90            data: self.into(),
91            generics: generics.map(Into::into),
92        }
93    }
94}
95
96impl<'a, Gen> AttachGenerics<'a> for MaybeBorrowed<'a, Gen> {
97    type Output = MaybeBorrowed<'a, Gen>;
98
99    fn with_optional_generics<G>(self, generics: Option<G>) -> WithGenerics<'a, Self::Output>
100    where
101        G: Into<MaybeBorrowed<'a, syn::Generics>>,
102    {
103        WithGenerics {
104            data: self,
105            generics: generics.map(Into::into),
106        }
107    }
108}
109
110impl<'a, Gen, A> AttachGenerics<'a> for Assert<'a, Gen, A>
111where
112    Gen: Generatable<'a, A>,
113    A: 'a + TidAble<'a>,
114{
115    type Output = Assert<'a, Gen, A>;
116
117    fn with_optional_generics<G>(self, generics: Option<G>) -> WithGenerics<'a, Self::Output>
118    where
119        G: Into<MaybeBorrowed<'a, syn::Generics>>,
120    {
121        WithGenerics {
122            data: self,
123            generics: generics.map(Into::into),
124        }
125    }
126}
127
128impl<'a, G, A> Assertable<'a> for WithGenerics<'a, Assert<'a, G, A>>
129where
130    G: Generatable<'a, A> + Eq + Ord,
131    A: Eq + Ord + TidAble<'a>,
132{
133    type Output = RawAssert<'a, G, A>;
134
135    fn do_assert(self) -> RawAssert<'a, G, A> {
136        RawAssert {
137            template: self.data.template,
138            generics: check_generics(self.generics),
139            assert: self.data.assert,
140        }
141    }
142}
143
144fn check_generics(
145    generics: Option<MaybeBorrowed<syn::Generics>>,
146) -> Option<MaybeBorrowed<syn::Generics>> {
147    generics.and_then(|f| {
148        if f.lt_token.is_some()
149            && f.gt_token.is_some()
150            && !f.params.is_empty()
151            && f.where_clause
152                .as_ref()
153                .is_some_and(|w| !w.predicates.is_empty())
154        {
155            Some(f)
156        } else {
157            None
158        }
159    })
160}