1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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
        }
    })
}