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
use crate::{
    assert::Assert, assertable::Assertable, generatable::Generatable,
    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>>;
}

impl<'a, Gen> AttachGenerics<'a> for Gen
where
    Gen: Generatable<'a>,
{
    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: MaybeBorrowed::Owned(self),
            generics: generics.map(Into::into),
        }
    }
}

impl<'a, Gen> AttachGenerics<'a> for MaybeBorrowed<'a, Gen>
where
    Gen: Generatable<'a>,
{
    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> AttachGenerics<'a> for Assert<'a, Gen>
where
    Gen: Generatable<'a>,
{
    type Output = Assert<'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, G> Assertable<'a> for WithGenerics<'a, Assert<'a, G>>
where
    G: Generatable<'a> + Eq + Ord,
    G::Assert: Eq + Ord,
{
    type Output = RawAssert<'a, G>;

    fn do_assert(self) -> RawAssert<'a, G> {
        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
        }
    })
}