proc_macro_assertions/generatable/
type.rs

1use std::iter;
2
3use better_any::{Tid, TidAble};
4use proc_macro2::{Span, TokenStream};
5use quote::quote;
6use syn::Ident;
7
8use crate::{
9    context::Context,
10    maybe_borrowed::MaybeBorrowed,
11    passed_data::{PassedData, WithData},
12    token_cmp_wrapper::TokenCmpWrapper,
13};
14
15use super::{Generatable, StaticTid};
16/// A trait Generatable / Template. This asserts that some type implements some trait.
17#[derive(Tid, PartialEq, Eq, PartialOrd, Ord)]
18pub struct Type<'a> {
19    #[doc = r"The type to check the asserted type equals"]
20    pub r#type: MaybeBorrowed<'a, TokenCmpWrapper<syn::Type>>,
21    /// The amount of generics the type should have
22    pub generics_count: usize,
23}
24
25impl<'a> Type<'a> {
26    /// Creates a new type template from some type to be checked against.
27    /// Takes any T that can be turned into a `MaybeBorrowed` type, to support
28    /// references as well as owned types
29    pub fn new<T>(r#type: T, generics_count: usize) -> Self
30    where
31        T: Into<MaybeBorrowed<'a, TokenCmpWrapper<syn::Type>>>,
32    {
33        Self {
34            r#type: r#type.into(),
35            generics_count,
36        }
37    }
38
39    #[must_use]
40    pub fn from_owned(r#type: syn::Type, generics_count: usize) -> Self {
41        Self::new(r#type, generics_count)
42    }
43
44    #[must_use]
45    pub fn from_borrowed(r#type: &'a syn::Type, generics_count: usize) -> Self {
46        Self::new(r#type, generics_count)
47    }
48}
49
50impl<'a> Generatable<'a, TokenCmpWrapper<syn::Type>> for Type<'a> {
51    type GeneratableData = Ident;
52    type TemplateData = ();
53
54    const EMITS_NON_CONSTANT_CODE: bool = false;
55
56    fn generatable(
57        Context {
58            ident_generator, ..
59        }: &mut Context,
60    ) -> PassedData<Self::GeneratableData> {
61        let ident = ident_generator.prefixed("assert_type_eq");
62        let type_eq_ident = ident_generator.prefixed("TypeEq");
63
64        quote! {
65            trait #type_eq_ident {
66                type This: ?Sized;
67            }
68            impl<T: ?Sized> #type_eq_ident for T {
69                type This = Self;
70            }
71
72            fn #ident<T, U>()
73            where
74                T: ?Sized + TypeEq<This = U>,
75                U: ?Sized,
76            {
77            }
78        }
79        .with_data(ident)
80    }
81
82    fn assert(
83        &self,
84        _context: &mut Context,
85        (assert_type_eq, _): (&Self::GeneratableData, &Self::TemplateData),
86        to_assert: &TokenCmpWrapper<syn::Type>,
87    ) -> Option<TokenStream> {
88        let type_bound = &*self.r#type;
89
90        let generics = (self.generics_count > 0).then(|| {
91            let generic_ident = Ident::new("_", Span::call_site());
92            let generic_ident_iter = iter::repeat(generic_ident).take(self.generics_count);
93
94            quote! {
95                <#(#generic_ident_iter),*>
96            }
97        });
98
99        Some(quote! {
100            #assert_type_eq::<#to_assert,#type_bound #generics>();
101        })
102    }
103
104    fn template(
105        &self,
106        _context: &mut Context,
107        _passed: &Self::GeneratableData,
108    ) -> PassedData<Self::TemplateData>
109    where
110        Self::TemplateData: Default,
111    {
112        PassedData::default()
113    }
114}
115
116impl<'a> Generatable<'a, TokenCmpWrapper<StaticTid<syn::Ident>>> for Type<'a> {
117    type GeneratableData = Ident;
118    type TemplateData = ();
119
120    const EMITS_NON_CONSTANT_CODE: bool = true;
121
122    fn generatable(
123        Context {
124            ident_generator, ..
125        }: &mut Context,
126    ) -> PassedData<Self::GeneratableData> {
127        let ident = ident_generator.prefixed("assert_type_eq");
128        let type_eq_ident = ident_generator.prefixed("TypeEq");
129
130        quote! {
131            trait #type_eq_ident {
132                type This: ?Sized;
133            }
134            impl<T: ?Sized> #type_eq_ident for T {
135                type This = Self;
136            }
137
138            fn #ident<T, U>(_v: U)
139            where
140                U: ?Sized + TypeEq<This = U>,
141                T: ?Sized,
142            {
143            }
144        }
145        .with_data(ident)
146    }
147
148    fn assert(
149        &self,
150        _context: &mut Context,
151        (assert_type_eq, _): (&Self::GeneratableData, &Self::TemplateData),
152        to_assert: &TokenCmpWrapper<StaticTid<syn::Ident>>,
153    ) -> Option<TokenStream> {
154        let type_bound = &*self.r#type;
155
156        let generics = (self.generics_count > 0).then(|| {
157            let generic_ident = Ident::new("_", Span::call_site());
158            let generic_ident_iter = iter::repeat(generic_ident).take(self.generics_count);
159
160            quote! {
161                <#(#generic_ident_iter),*>
162            }
163        });
164
165        Some(quote! {
166            #assert_type_eq::<#type_bound #generics,_>(#to_assert);
167        })
168    }
169
170    fn template(
171        &self,
172        _context: &mut Context,
173        _passed: &Self::GeneratableData,
174    ) -> PassedData<Self::TemplateData>
175    where
176        Self::TemplateData: Default,
177    {
178        PassedData::default()
179    }
180}
181
182#[cfg(test)]
183mod test {
184    use crate::ident_generator::MockIdentGenerator;
185    use quote::quote;
186    use syn::parse_quote;
187
188    use super::*;
189
190    #[test]
191    fn generate_generatable() {
192        let mut mock_ident_gen = MockIdentGenerator::new();
193
194        mock_ident_gen.push_ident("mock_1");
195
196        let mut context = Context::new(&mut mock_ident_gen);
197
198        assert_eq!(
199            <Type as Generatable<TokenCmpWrapper<syn::Type>>>::generatable(&mut context),
200            quote! {
201                trait TypeEq {
202                    type This: ?Sized;
203                }
204                impl<T: ?Sized> TypeEq for T {
205                    type This = Self;
206                }
207
208                fn assert_type_eq_mock_1<T, U>()
209                where
210                    T: ?Sized + TypeEq<This = U>,
211                    U: ?Sized,
212                {
213                }
214            }
215            .with_data(parse_quote!(assert_type_eq_mock_1)),
216        );
217
218        assert!(mock_ident_gen.has_no_idents_remaining());
219    }
220
221    #[test]
222    fn generate_template() {
223        let mut mock_ident_gen = MockIdentGenerator::new();
224        let mut context = Context::new(&mut mock_ident_gen);
225
226        assert_eq!(
227            <Type as Generatable<TokenCmpWrapper<syn::Type>>>::template(
228                &Type {
229                    r#type: (&syn::Type::Path(syn::parse_quote!(Type))).into(),
230                    generics_count: 2,
231                },
232                &mut context,
233                &parse_quote!(assert_type_eq_mock_1)
234            ),
235            PassedData::<()>::default(),
236        );
237        assert!(mock_ident_gen.was_not_called());
238    }
239
240    #[test]
241    fn generate_assert() {
242        let mut mock_ident_gen = MockIdentGenerator::new();
243        let mut context = Context::new(&mut mock_ident_gen);
244
245        assert_eq!(
246            <Type as Generatable<TokenCmpWrapper<syn::Type>>>::assert(
247                &Type {
248                    r#type: (&syn::Type::Path(syn::parse_quote!(Type))).into(),
249                    generics_count: 2,
250                },
251                &mut context,
252                (&parse_quote!(assert_type_eq_mock_1), &()),
253                &syn::Type::Verbatim("Test".parse().unwrap()).into()
254            )
255            .map(|x| x.to_string()),
256            Some(quote! {
257                assert_type_eq_mock_1::<Test,Type<_,_>>();
258            })
259            .map(|x| x.to_string())
260        );
261        assert!(mock_ident_gen.was_not_called());
262    }
263}