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