1use std::io::Write;
2
3use proc_macro::TokenStream as TokenStream1;
4use proc_macro2::{Span, TokenStream};
5use quote::{quote, ToTokens, TokenStreamExt};
6use syn::{parse_macro_input, AttrStyle, DeriveInput, Ident, Lifetime, Type};
7
8#[proc_macro_attribute]
9pub fn test(_attr: TokenStream1, item: TokenStream1) -> TokenStream1 {
10 let fn_item = parse_macro_input!(item as syn::ItemFn);
11 let fn_name = fn_item.sig.ident;
12 let test_name = fn_name.to_string();
13 let test_name_ident = syn::Ident::new(&test_name.to_uppercase(), fn_name.span());
14 let test_body = fn_item.block;
15 let unsafe_token = fn_item.sig.unsafety;
16 quote! {
17 #[::linkme::distributed_slice(crate::test::TLUA_TESTS)]
18 static #test_name_ident: crate::test::TestCase = crate::test::TestCase {
19 name: ::std::concat!(::std::module_path!(), "::", #test_name),
20 f: || #unsafe_token #test_body,
21 };
22 }
23 .into()
24}
25
26fn proc_macro_derive_push_impl(
27 input: proc_macro::TokenStream,
28 is_push_into: bool,
29) -> proc_macro::TokenStream {
30 let input = parse_macro_input!(input as DeriveInput);
31 let name = &input.ident;
34 let info = Info::new(&input);
35 let ctx = Context::with_generics(&input.generics).set_is_push_into(is_push_into);
36 let (lifetimes, types, consts) = split_generics(&input.generics);
37 let (_, generics, where_clause) = input.generics.split_for_impl();
38 let type_bounds = where_clause.map(|w| &w.predicates);
39 let as_lua_bounds = info.push_bounds(&ctx);
40 let push_code = info.push();
41 let PushVariant {
42 push_fn,
43 push,
44 push_one,
45 } = ctx.push_variant();
46 let l = ctx.as_lua_type_param;
47
48 let expanded = quote! {
49 #[automatically_derived]
50 impl<#(#lifetimes,)* #(#types,)* #l, #(#consts,)*> tlua::#push<#l> for #name #generics
51 where
52 #l: tlua::AsLua,
53 #as_lua_bounds
54 #type_bounds
55 {
56 type Err = tlua::Void;
57
58 fn #push_fn -> ::std::result::Result<tlua::PushGuard<#l>, (Self::Err, #l)> {
59 Ok(#push_code)
60 }
61 }
62
63 impl<#(#lifetimes,)* #(#types,)* #l, #(#consts,)*> tlua::#push_one<#l> for #name #generics
64 where
65 #l: tlua::AsLua,
66 #as_lua_bounds
67 #type_bounds
68 {
69 }
70 };
71
72 expanded.into()
73}
74
75#[proc_macro_derive(Push)]
76pub fn proc_macro_derive_push(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
77 proc_macro_derive_push_impl(input, false)
78}
79
80#[proc_macro_derive(PushInto)]
81pub fn proc_macro_derive_push_into(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
82 proc_macro_derive_push_impl(input, true)
83}
84
85#[proc_macro_derive(LuaRead)]
86pub fn proc_macro_derive_lua_read(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
87 let input = parse_macro_input!(input as DeriveInput);
88 let name = &input.ident;
89 let info = Info::new(&input);
90 let ctx = Context::with_generics(&input.generics);
91 let (lifetimes, types, consts) = split_generics(&input.generics);
92 let (_, generics, where_clause) = input.generics.split_for_impl();
93 let type_bounds = where_clause.map(|w| &w.predicates);
94 let as_lua_bounds = info.read_bounds(&ctx);
95 let read_at_code = info.read(&ctx);
96 let maybe_n_values_expected = info.n_values(&ctx);
97 let maybe_lua_read = info.read_top(&ctx);
98
99 let l = ctx.as_lua_type_param;
100
101 let expanded = quote! {
102 #[automatically_derived]
103 impl<#(#lifetimes,)* #(#types,)* #l, #(#consts,)*> tlua::LuaRead<#l> for #name #generics
104 where
105 #l: tlua::AsLua,
106 #as_lua_bounds
107 #type_bounds
108 {
109 #maybe_n_values_expected
110
111 #maybe_lua_read
112
113 #[inline(always)]
114 fn lua_read_at_position(__lua: #l, __index: ::std::num::NonZeroI32)
115 -> tlua::ReadResult<Self, #l>
116 {
117 Self::lua_read_at_maybe_zero_position(__lua, __index.into())
118 }
119
120 fn lua_read_at_maybe_zero_position(__lua: #l, __index: i32)
121 -> tlua::ReadResult<Self, #l>
122 {
123 #read_at_code
124 }
125 }
126 };
127
128 expanded.into()
129}
130
131macro_rules! ident {
132 ($str:literal) => {
133 Ident::new($str, Span::call_site())
134 };
135 ($($args:tt)*) => {
136 Ident::new(&format!($($args)*), Span::call_site())
137 };
138}
139
140enum Info<'a> {
141 Struct(FieldsInfo<'a>),
142 Enum(VariantsInfo<'a>),
143}
144
145impl<'a> Info<'a> {
146 fn new(input: &'a DeriveInput) -> Self {
147 match input.data {
148 syn::Data::Struct(ref s) => {
149 if let Some(fields) = FieldsInfo::new(&s.fields) {
150 Self::Struct(fields)
151 } else {
152 unimplemented!("standalone unit structs aren't supproted yet")
153 }
154 }
155 syn::Data::Enum(ref e) => Self::Enum(VariantsInfo::new(e)),
156 syn::Data::Union(_) => unimplemented!("unions will never be supported"),
157 }
158 }
159
160 fn push(&self) -> TokenStream {
161 match self {
162 Self::Struct(f) => {
163 let fields = f.pattern();
164 let push_fields = f.push();
165 quote! {
166 match self {
167 Self #fields => #push_fields,
168 }
169 }
170 }
171 Self::Enum(v) => {
172 let push_variants = v.variants.iter().map(VariantInfo::push).collect::<Vec<_>>();
173 quote! {
174 match self {
175 #( #push_variants )*
176 }
177 }
178 }
179 }
180 }
181
182 fn push_bounds(&self, ctx: &Context) -> TokenStream {
183 let l = &ctx.as_lua_type_param;
184 let PushVariant { push, push_one, .. } = ctx.push_variant();
185
186 let field_bounds = |info: &FieldsInfo| match info {
187 FieldsInfo::Named {
188 field_types: ty, ..
189 } => {
190 let ty = ty.iter().filter(|ty| ctx.is_generic(ty));
191 quote! {
192 #(
193 #ty: tlua::#push_one<tlua::LuaState>,
194 tlua::Void: ::std::convert::From<<#ty as tlua::#push<tlua::LuaState>>::Err>,
195 )*
196 }
197 }
198 FieldsInfo::Unnamed {
199 field_types: ty, ..
200 } if ty.iter().any(|ty| ctx.is_generic(ty)) => {
201 quote! {
202 (#(#ty),*): tlua::#push<#l>,
203 tlua::Void: ::std::convert::From<<(#(#ty),*) as tlua::#push<#l>>::Err>,
204 }
205 }
206 FieldsInfo::Unnamed { .. } => {
207 quote! {}
208 }
209 };
210 match self {
211 Self::Struct(f) => field_bounds(f),
212 Self::Enum(v) => {
213 let bound = v.variants.iter().flat_map(|v| &v.info).map(field_bounds);
214 quote! {
215 #(#bound)*
216 }
217 }
218 }
219 }
220
221 fn read(&self, ctx: &Context) -> TokenStream {
222 match self {
223 Self::Struct(f) => f.read_as(None),
224 Self::Enum(v) => {
225 let mut n_vals = vec![];
226 let mut read_and_maybe_return_variant = vec![];
227 for variant in &v.variants {
228 n_vals.push(if let Some(ref fields) = variant.info {
229 fields.n_values(ctx)
230 } else {
231 quote! { 1 }
232 });
233 read_and_maybe_return_variant.push(variant.read_and_maybe_return());
234 }
235 quote! {
236 let mut errors: ::std::collections::LinkedList<tlua::WrongType> = Default::default();
237 #(
238 let n_vals = #n_vals;
239 let __lua = #read_and_maybe_return_variant;
240 )*
241 let e = tlua::WrongType::info("reading any of the variants")
242 .expected_type::<Self>()
243 .actual("something else")
244 .subtypes(errors);
245 Err((__lua, e))
246 }
247 }
248 }
249 }
250
251 fn read_bounds(&self, ctx: &Context) -> TokenStream {
252 let l = &ctx.as_lua_type_param;
253 let lt = &ctx.as_lua_lifetime_param;
254
255 let field_bounds = |info: &FieldsInfo| {
256 match info {
257 FieldsInfo::Named {
258 field_types: ty, ..
259 } => {
260 let ty = ty.iter().filter(|ty| ctx.is_generic(ty));
263 quote! {
264 #( #ty: for<#lt> tlua::LuaRead<tlua::PushGuard<&#lt #l>>, )*
265 }
266 }
267 FieldsInfo::Unnamed {
268 field_types: ty, ..
269 } if ty.iter().any(|ty| ctx.is_generic(ty)) => {
270 quote! {
273 (#(#ty),*): tlua::LuaRead<#l>,
274 }
275 }
276 FieldsInfo::Unnamed { .. } => {
277 quote! {}
280 }
281 }
282 };
283 match self {
284 Self::Struct(f) => field_bounds(f),
285 Self::Enum(v) => {
286 let bound = v.variants.iter().flat_map(|v| &v.info).map(field_bounds);
287 quote! {
288 #(#bound)*
289 }
290 }
291 }
292 }
293
294 fn read_top(&self, ctx: &Context) -> TokenStream {
295 match self {
296 Self::Struct(_) => quote! {},
297 Self::Enum(v) => {
298 let mut n_vals = vec![];
299 let mut read_and_maybe_return = vec![];
300 for variant in &v.variants {
301 n_vals.push(if let Some(ref fields) = variant.info {
302 fields.n_values(ctx)
303 } else {
304 quote! { 1 }
305 });
306 read_and_maybe_return.push(variant.read_and_maybe_return());
307 }
308 let l = &ctx.as_lua_type_param;
309 quote! {
310 fn lua_read(__lua: #l) -> tlua::ReadResult<Self, #l> {
311 let top = unsafe { tlua::ffi::lua_gettop(__lua.as_lua()) };
312 let mut errors: ::std::collections::LinkedList<tlua::WrongType> = Default::default();
313 #(
314 let n_vals = #n_vals;
315 let __lua = if top >= n_vals {
316 let __index = top - n_vals + 1;
317 #read_and_maybe_return
318 } else {
319 __lua
320 };
321 )*
322 let e = tlua::WrongType::info("reading any of the variants")
323 .expected_type::<Self>()
324 .actual("something else")
325 .subtypes(errors);
326 Err((__lua, e))
327 }
328 }
329 }
330 }
331 }
332
333 fn n_values(&self, ctx: &Context) -> TokenStream {
334 match self {
335 Self::Struct(fields) => {
336 let n_values = fields.n_values(ctx);
337 quote! {
338 #[inline(always)]
339 fn n_values_expected() -> i32 {
340 #n_values
341 }
342 }
343 }
344 Self::Enum(_) => {
345 quote! {}
346 }
347 }
348 }
349}
350
351enum FieldsInfo<'a> {
352 Named {
353 n_rec: i32,
354 field_names: Vec<String>,
355 field_idents: Vec<&'a Ident>,
356 field_types: Vec<&'a Type>,
357 },
358 Unnamed {
359 field_idents: Vec<Ident>,
360 field_types: Vec<&'a syn::Type>,
361 },
362}
363
364impl<'a> FieldsInfo<'a> {
365 fn new(fields: &'a syn::Fields) -> Option<Self> {
366 match &fields {
367 syn::Fields::Named(ref fields) => {
368 let n_fields = fields.named.len();
369 let mut field_names = Vec::with_capacity(n_fields);
370 let mut field_idents = Vec::with_capacity(n_fields);
371 let mut field_types = Vec::with_capacity(n_fields);
372 for field in fields.named.iter() {
373 let ident = field.ident.as_ref().unwrap();
374 field_names.push(ident.to_string().trim_start_matches("r#").into());
375 field_idents.push(ident);
376 field_types.push(&field.ty);
377 }
378
379 Some(Self::Named {
380 field_names,
381 field_idents,
382 field_types,
383 n_rec: n_fields as _,
384 })
385 }
386 syn::Fields::Unnamed(ref fields) => {
387 let mut field_idents = Vec::with_capacity(fields.unnamed.len());
388 let mut field_types = Vec::with_capacity(fields.unnamed.len());
389 for (field, i) in fields.unnamed.iter().zip(0..) {
390 field_idents.push(ident!("field_{}", i));
391 field_types.push(&field.ty);
392 }
393
394 Some(Self::Unnamed {
395 field_idents,
396 field_types,
397 })
398 }
399 syn::Fields::Unit => None,
402 }
403 }
404
405 fn push(&self) -> TokenStream {
406 match self {
407 Self::Named {
408 field_names,
409 field_idents,
410 n_rec,
411 ..
412 } => {
413 quote! {
414 unsafe {
415 tlua::ffi::lua_createtable(__lua.as_lua(), 0, #n_rec);
416 #(
417 tlua::AsLua::push_one(__lua.as_lua(), #field_idents)
418 .assert_one_and_forget();
419 tlua::ffi::lua_setfield(
420 __lua.as_lua(), -2, ::std::concat!(#field_names, "\0").as_ptr() as _
421 );
422 )*
423 tlua::PushGuard::new(__lua, 1)
424 }
425 }
426 }
427 Self::Unnamed { field_idents, .. } => match field_idents.len() {
428 0 => unimplemented!("unit structs are not supported yet"),
429 1 => {
430 let field_ident = &field_idents[0];
431 quote! {
432 tlua::AsLua::push(__lua, #field_ident)
433 }
434 }
435 _ => {
436 quote! {
437 tlua::AsLua::push(__lua, ( #( #field_idents, )* ))
438 }
439 }
440 },
441 }
442 }
443
444 fn read_as(&self, name: Option<TokenStream>) -> TokenStream {
445 let is_variant = name.is_some();
446 let name = name.unwrap_or_else(|| quote! { Self });
447 match self {
448 FieldsInfo::Named {
449 field_idents,
450 field_names,
451 field_types,
452 ..
453 } => {
454 let expected = if is_variant {
455 let mut msg = vec![];
456 write!(&mut msg, "struct with fields {{").unwrap();
457 if let Some((n, t)) = field_names.iter().zip(field_types).next() {
458 write!(&mut msg, " {}: {}", n, quote! { #t }).unwrap();
459 }
460 for (n, t) in field_names.iter().zip(field_types).skip(1) {
461 write!(&mut msg, ", {}: {}", n, quote! { #t }).unwrap();
462 }
463 write!(&mut msg, " }}").unwrap();
464 let msg = String::from_utf8(msg).unwrap();
465 quote! { .expected(#msg) }
466 } else {
467 quote! { .expected_type::<Self>() }
468 };
469 quote! {
470 let t: tlua::LuaTable<_> = tlua::AsLua::read_at(__lua, __index)
471 .map_err(|(lua, err)| {
472 let err = err.when("converting Lua value to struct")
473 .expected("Lua table");
474 (lua, err)
475 })?;
476 Ok(
477 #name {
478 #(
479 #field_idents: match tlua::Index::try_get(&t, #field_names) {
480 Ok(v) => v,
481 Err(err) => {
482 let l = t.into_inner();
483 let mut e = tlua::WrongType::info(
484 "converting Lua table to struct"
485 ) #expected;
486 match err {
487 tlua::LuaError::WrongType(subtype) => {
488 let actual_msg = ::std::concat!(
489 "wrong field type for key '",
490 #field_names,
491 "'",
492 );
493 e = e.actual(actual_msg).subtype(subtype);
494 }
495 other => {
496 e = e.actual(format!(
497 "error in meta method: {}", other
498 ));
499 }
500 }
501 return Err((l, e))
502 },
503 },
504 )*
505 }
506 )
507 }
508 }
509 FieldsInfo::Unnamed { field_idents, .. } => {
510 quote! {
511 let (#(#field_idents,)*) = tlua::AsLua::read_at(__lua, __index)?;
512 Ok(
513 #name(#(#field_idents,)*)
514 )
515 }
516 }
517 }
518 }
519
520 fn pattern(&self) -> TokenStream {
521 match self {
522 Self::Named { field_idents, .. } => {
523 quote! {
524 { #( #field_idents, )* }
525 }
526 }
527 Self::Unnamed { field_idents, .. } => {
528 quote! {
529 ( #( #field_idents, )* )
530 }
531 }
532 }
533 }
534
535 fn n_values(&self, ctx: &Context) -> TokenStream {
536 match self {
537 Self::Named { .. } => {
538 quote! { 1 }
540 }
541 Self::Unnamed {
542 field_types: ty, ..
543 } if !ty.is_empty() => {
544 let l = &ctx.as_lua_type_param;
545 quote! {
547 <(#(#ty),*) as tlua::LuaRead<#l>>::n_values_expected()
548 }
549 }
550 Self::Unnamed { .. } => {
551 quote! { 1 }
554 }
555 }
556 }
557}
558
559struct VariantsInfo<'a> {
560 variants: Vec<VariantInfo<'a>>,
561}
562
563struct VariantInfo<'a> {
564 name: &'a Ident,
565 info: Option<FieldsInfo<'a>>,
566}
567
568impl<'a> VariantsInfo<'a> {
569 fn new(data: &'a syn::DataEnum) -> Self {
570 let variants = data
571 .variants
572 .iter()
573 .map(
574 |syn::Variant {
575 ref ident,
576 ref fields,
577 ..
578 }| VariantInfo {
579 name: ident,
580 info: FieldsInfo::new(fields),
581 },
582 )
583 .collect();
584
585 Self { variants }
586 }
587}
588
589impl VariantInfo<'_> {
590 fn push(&self) -> TokenStream {
591 let Self { name, info } = self;
592 if let Some(info) = info {
593 let fields = info.pattern();
594 let push_fields = info.push();
595 quote! {
596 Self::#name #fields => #push_fields,
597 }
598 } else {
599 let value = name.to_string().to_lowercase();
600 quote! {
601 Self::#name => {
602 tlua::AsLua::push_one(__lua.as_lua(), #value)
603 .assert_one_and_forget();
604 unsafe { tlua::PushGuard::new(__lua, 1) }
605 }
606 }
607 }
608 }
609
610 fn read_and_maybe_return(&self) -> TokenStream {
611 let read_variant = self.read();
612 let pattern = self.pattern();
613 let constructor = self.constructor();
614 let (guard, catch_all) = self.optional_match();
615 let name = self.name.to_string();
616 quote! {
617 match #read_variant {
618 ::std::result::Result::Ok(#pattern) #guard
619 => return ::std::result::Result::Ok(#constructor),
620 #catch_all
621 ::std::result::Result::Err((__lua, e)) => {
622 let mut e = tlua::WrongType::info("reading enum variant")
623 .expected(#name)
624 .subtype(e);
625 if let Some(i) = ::std::num::NonZeroI32::new(__index) {
626 e = e.actual_multiple_lua_at(&__lua, i, n_vals)
627 } else {
628 e = e.actual("no value")
629 }
630 errors.push_back(e);
631 __lua
632 }
633 }
634 }
635 }
636
637 fn read(&self) -> TokenStream {
638 let Self { name, info } = self;
639 match info {
640 Some(s @ FieldsInfo::Named { .. }) => {
641 let read_struct = s.read_as(Some(quote! { Self::#name }));
642 quote! {
643 (|| { #read_struct })()
644 }
645 }
646 Some(FieldsInfo::Unnamed { .. }) => {
647 quote! {
648 tlua::AsLua::read_at(__lua, __index)
649 }
650 }
651 None => {
652 quote! {
653 tlua::AsLua::read_at::<tlua::StringInLua<_>>(__lua, __index)
654 }
655 }
656 }
657 }
658
659 fn pattern(&self) -> TokenStream {
660 let Self { info, .. } = self;
661 match info {
662 Some(FieldsInfo::Named { .. }) | None => quote! { v },
663 Some(FieldsInfo::Unnamed { field_idents, .. }) => match field_idents.len() {
664 0 => unimplemented!("unit structs aren't supported yet"),
665 1 => quote! { v },
666 _ => quote! { ( #(#field_idents,)* ) },
667 },
668 }
669 }
670
671 fn constructor(&self) -> TokenStream {
672 let Self { name, info } = self;
673 match info {
674 Some(FieldsInfo::Named { .. }) => quote! { v },
675 Some(FieldsInfo::Unnamed { field_idents, .. }) => match field_idents.len() {
676 0 => quote! { Self::#name },
677 1 => quote! { Self::#name(v) },
678 _ => quote! { Self::#name(#(#field_idents,)*) },
679 },
680 None => quote! { Self::#name },
681 }
682 }
683
684 fn optional_match(&self) -> (TokenStream, TokenStream) {
685 let Self { name, info } = self;
686 let value = name.to_string().to_lowercase();
687 if info.is_none() {
688 let expected = format!("case incensitive match with '{value}'");
689 (
690 quote! {
691 if {
692 let mut v_count = 0;
693 v.chars()
694 .flat_map(char::to_lowercase)
695 .zip(
696 #value.chars()
697 .map(::std::option::Option::Some)
698 .chain(::std::iter::repeat(::std::option::Option::None))
699 )
700 .all(|(l, r)| {
701 v_count += 1;
702 r.map(|r| l == r).unwrap_or(false)
703 }) && v_count == #value.len()
704 }
705 },
706 quote! {
707 ::std::result::Result::Ok(v) => {
708 let e = tlua::WrongType::info("reading unit struct")
709 .expected(#expected)
710 .actual(format!("'{}'", &*v));
711 errors.push_back(e);
712 v.into_inner()
713 },
714 },
715 )
716 } else {
717 (quote! {}, quote! {})
718 }
719 }
720}
721
722struct Context<'a> {
723 as_lua_type_param: Ident,
724 as_lua_lifetime_param: Lifetime,
725 type_params: Vec<&'a Ident>,
726 is_push_into: bool,
727}
728
729struct PushVariant {
730 push_fn: TokenStream,
731 push: syn::Path,
732 push_one: syn::Path,
733}
734impl<'a> Context<'a> {
735 fn new() -> Self {
736 Self {
737 as_lua_type_param: Ident::new("__AsLuaTypeParam", Span::call_site()),
738 as_lua_lifetime_param: Lifetime::new("'as_lua_life_time_param", Span::call_site()),
739 type_params: Vec::new(),
740 is_push_into: false,
741 }
742 }
743
744 fn with_generics(generics: &'a syn::Generics) -> Self {
745 Self {
746 type_params: generics.type_params().map(|tp| &tp.ident).collect(),
747 ..Self::new()
748 }
749 }
750
751 fn set_is_push_into(self, is_push_into: bool) -> Self {
752 Self {
753 is_push_into,
754 ..self
755 }
756 }
757
758 fn push_variant(&self) -> PushVariant {
759 let l = &self.as_lua_type_param;
760 if self.is_push_into {
761 PushVariant {
762 push_fn: quote! {
763 push_into_lua(self, __lua: #l)
764 },
765 push: ident!("PushInto").into(),
766 push_one: ident!("PushOneInto").into(),
767 }
768 } else {
769 PushVariant {
770 push_fn: quote! {
771 push_to_lua(&self, __lua: #l)
772 },
773 push: ident!("Push").into(),
774 push_one: ident!("PushOne").into(),
775 }
776 }
777 }
778
779 fn is_generic(&self, ty: &Type) -> bool {
780 struct GenericTypeVisitor<'a> {
781 is_generic: bool,
782 type_params: &'a [&'a Ident],
783 }
784 impl<'ast> syn::visit::Visit<'ast> for GenericTypeVisitor<'_> {
785 fn visit_type_impl_trait(&mut self, _: &'ast syn::TypeImplTrait) {
788 self.is_generic = true;
789 }
790
791 fn visit_type_path(&mut self, tp: &'ast syn::TypePath) {
792 for &typar in self.type_params {
793 if tp.path.is_ident(typar) {
794 self.is_generic = true;
795 return;
796 }
797 }
798 syn::visit::visit_type_path(self, tp)
799 }
800 }
801
802 let mut visitor = GenericTypeVisitor {
803 is_generic: false,
804 type_params: &self.type_params,
805 };
806 syn::visit::visit_type(&mut visitor, ty);
807 visitor.is_generic
808 }
809}
810
811#[derive(Copy, Clone)]
812struct ImplTypeParam<'a>(&'a syn::TypeParam);
813impl ToTokens for ImplTypeParam<'_> {
814 fn to_tokens(&self, tokens: &mut TokenStream) {
815 let param = self.0;
817 tokens.append_all(
818 param
819 .attrs
820 .iter()
821 .filter(|attr| matches!(attr.style, AttrStyle::Outer)),
822 );
823 param.ident.to_tokens(tokens);
824 if !param.bounds.is_empty() {
825 if let Some(colon) = ¶m.colon_token {
826 colon.to_tokens(tokens);
827 }
828 param.bounds.to_tokens(tokens);
829 }
830 }
831}
832
833#[derive(Copy, Clone)]
834struct ImplConstParam<'a>(&'a syn::ConstParam);
835impl ToTokens for ImplConstParam<'_> {
836 fn to_tokens(&self, tokens: &mut TokenStream) {
837 let param = self.0;
839 tokens.append_all(
840 param
841 .attrs
842 .iter()
843 .filter(|attr| matches!(attr.style, AttrStyle::Outer)),
844 );
845 param.const_token.to_tokens(tokens);
846 param.ident.to_tokens(tokens);
847 param.colon_token.to_tokens(tokens);
848 param.ty.to_tokens(tokens);
849 }
850}
851
852fn split_generics(
853 generics: &syn::Generics,
854) -> (
855 Vec<&syn::LifetimeDef>,
856 Vec<ImplTypeParam>,
857 Vec<ImplConstParam>,
858) {
859 let mut res = (vec![], vec![], vec![]);
860 for param in &generics.params {
861 match param {
862 syn::GenericParam::Lifetime(l) => res.0.push(l),
863 syn::GenericParam::Type(t) => res.1.push(ImplTypeParam(t)),
864 syn::GenericParam::Const(c) => res.2.push(ImplConstParam(c)),
865 }
866 }
867 res
868}