proc_macro_assertions/generatable/
trait.rs1use better_any::{Tid, TidAble};
2use proc_macro2::TokenStream;
3use quote::{quote, ToTokens};
4use syn::{Ident, TraitBound, Type};
5
6use crate::{
7 context::Context,
8 into_template::IntoTemplate,
9 maybe_borrowed::{FromMaybeBorrowed, MaybeBorrowed},
10 passed_data::{PassedData, WithData},
11 token_cmp_wrapper::TokenCmpWrapper,
12};
13
14use super::{Generatable, StaticTid};
15
16#[derive(Tid)]
18pub struct Trait<'a> {
19 trait_bound: MaybeBorrowed<'a, TraitBound>,
21}
22
23impl<'a> Trait<'a> {
24 pub fn new<T>(trait_bound: T) -> Self
28 where
29 T: Into<MaybeBorrowed<'a, TraitBound>>,
30 {
31 Self {
32 trait_bound: trait_bound.into(),
33 }
34 }
35}
36
37impl<'a> FromMaybeBorrowed<'a, TraitBound> for Trait<'a> {
38 fn from_maybe_borrowed(from: MaybeBorrowed<'a, TraitBound>) -> Self {
39 Self::new(from)
40 }
41}
42
43impl<'a> IntoTemplate<'a> for TraitBound {
44 type Template = Trait<'a>;
45
46 fn into_template<T: crate::into_template::TypeEq<This = Self::Template>>(
47 self,
48 ) -> Self::Template {
49 Trait::new(self)
50 }
51}
52
53impl<'a> IntoTemplate<'a> for &'a TraitBound {
54 type Template = Trait<'a>;
55
56 fn into_template<T: crate::into_template::TypeEq<This = Self::Template>>(
57 self,
58 ) -> Self::Template {
59 Trait::new(self)
60 }
61}
62
63impl<'a> PartialOrd for Trait<'a> {
64 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
65 Some(Ord::cmp(self, other))
66 }
67}
68
69impl<'a> Ord for Trait<'a> {
70 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
71 Ord::cmp(
72 &self.trait_bound.to_token_stream().to_string(),
73 &other.trait_bound.to_token_stream().to_string(),
74 )
75 }
76}
77
78impl<'a> PartialEq for Trait<'a> {
79 fn eq(&self, other: &Self) -> bool {
80 PartialEq::eq(
81 &self.trait_bound.to_token_stream().to_string(),
82 &other.trait_bound.to_token_stream().to_string(),
83 )
84 }
85}
86
87impl<'a> Eq for Trait<'a> {}
88
89impl<'a> Generatable<'a, TokenCmpWrapper<Type>> for Trait<'a> {
90 type GeneratableData = ();
91 type TemplateData = Ident;
92
93 const EMITS_NON_CONSTANT_CODE: bool = false;
94
95 fn template(
96 &self,
97 Context {
98 ident_generator, ..
99 }: &mut Context,
100 _passed: &Self::GeneratableData,
101 ) -> PassedData<Self::TemplateData> {
102 let fn_ident = ident_generator.prefixed("assert_trait_bound");
103 let trait_bound = &*self.trait_bound;
104
105 quote! {
106 fn #fn_ident<T: #trait_bound>() {}
107 }
108 .with_data(fn_ident)
109 }
110
111 fn assert(
112 &self,
113 _context: &mut Context,
114 (_, assert_trait_bound): (&Self::GeneratableData, &Self::TemplateData),
115 to_assert: &TokenCmpWrapper<syn::Type>,
116 ) -> Option<TokenStream> {
117 Some(quote! {
118 #assert_trait_bound::<#to_assert>();
119 })
120 }
121
122 fn generatable(_context: &mut Context) -> PassedData<Self::GeneratableData>
123 where
124 Self: Sized,
125 {
126 PassedData::default()
127 }
128}
129
130impl<'a> Generatable<'a, StaticTid<Ident>> for Trait<'a> {
131 type GeneratableData = ();
132 type TemplateData = Ident;
133
134 const EMITS_NON_CONSTANT_CODE: bool = true;
135
136 fn template(
137 &self,
138 Context {
139 ident_generator, ..
140 }: &mut Context,
141 _passed: &Self::GeneratableData,
142 ) -> PassedData<Self::TemplateData> {
143 let fn_ident = ident_generator.prefixed("assert_trait_bound");
144 let trait_bound = &*self.trait_bound;
145
146 quote! {
147 fn #fn_ident<T: #trait_bound>(_v: T) {}
148 }
149 .with_data(fn_ident)
150 }
151
152 fn assert(
153 &self,
154 _context: &mut Context,
155 (_, assert_trait_bound): (&Self::GeneratableData, &Self::TemplateData),
156 to_assert: &StaticTid<proc_macro2::Ident>,
157 ) -> Option<TokenStream> {
158 Some(quote! {
159 #assert_trait_bound(#to_assert);
160 })
161 }
162
163 fn generatable(_context: &mut Context) -> PassedData<Self::GeneratableData>
164 where
165 Self: Sized,
166 {
167 PassedData::default()
168 }
169}
170
171#[cfg(test)]
172mod test {
173 use quote::quote;
174 use syn::parse_quote;
175
176 use crate::{
177 context::Context,
178 generatable::Generatable,
179 ident_generator::MockIdentGenerator,
180 maybe_borrowed::MaybeBorrowed,
181 passed_data::{PassedData, WithData},
182 token_cmp_wrapper::TokenCmpWrapper,
183 };
184
185 use super::Trait;
186
187 #[test]
188 fn generate_generatable() {
189 let mut mock_ident_gen = MockIdentGenerator::new();
190 let mut context = Context::new(&mut mock_ident_gen);
191
192 assert_eq!(
193 <Trait as Generatable<TokenCmpWrapper<syn::Type>>>::generatable(&mut context),
194 PassedData::default()
195 );
196 assert!(mock_ident_gen.was_not_called());
197 }
198
199 #[test]
200 fn generate_template() {
201 let mut mock_ident_gen = MockIdentGenerator::new();
202
203 mock_ident_gen.push_ident("mock_1");
204
205 let mut context = Context::new(&mut mock_ident_gen);
206 assert_eq!(
207 <Trait as Generatable<TokenCmpWrapper<syn::Type>>>::template(
208 &Trait {
209 trait_bound: MaybeBorrowed::Owned(parse_quote!(::test_mod::Test))
210 },
211 &mut context,
212 &()
213 ),
214 quote! {
215 fn assert_trait_bound_mock_1<T: ::test_mod::Test>() {}
216 }
217 .with_data(parse_quote!(assert_trait_bound_mock_1))
218 );
219 assert!(mock_ident_gen.has_no_idents_remaining());
220 }
221
222 #[test]
223 fn generate_assert() {
224 let mut mock_ident_gen = MockIdentGenerator::new();
225 let mut context = Context::new(&mut mock_ident_gen);
226
227 let test_type: syn::Type = parse_quote!(TestType);
228
229 assert_eq!(
230 <Trait as Generatable<TokenCmpWrapper<syn::Type>>>::assert(
231 &Trait {
232 trait_bound: MaybeBorrowed::Owned(parse_quote!(::test_mod::Test))
233 },
234 &mut context,
235 (&(), &parse_quote!(assert_trait_bound_mock_1)),
236 &test_type.into()
237 )
238 .as_ref()
239 .map(ToString::to_string),
240 Some(quote! {
241 assert_trait_bound_mock_1::<TestType>();
242 })
243 .as_ref()
244 .map(ToString::to_string)
245 );
246 assert!(mock_ident_gen.was_not_called());
247 }
248}