tlua_derive/
lib.rs

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    // TODO(gmoshkin): add an attribute to specify path to tlua module (see serde)
32    // TODO(gmoshkin): add support for custom type param bounds
33    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    // We skip default values for constant generics parameters
38    // because they are not supported in impl blocks. You may want to check
39    // https://git.picodata.io/picodata/picodata/tarantool-module/-/issues/219
40    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                    // Structs fields are read as values from the lua tables and
273                    // this is how `LuaTable::get` bounds it's return values
274                    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                    // Tuple structs are read as tuples, so we bound they're
283                    // fields as if they were a tuple
284                    quote! {
285                        (#(#ty),*): tlua::LuaRead<#l>,
286                    }
287                }
288                FieldsInfo::Unnamed { .. } => {
289                    // Unit structs (as vairants of enums) are read as strings
290                    // so no need for type bounds
291                    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            // TODO(gmoshkin): add attributes for changing string value, case
412            // sensitivity etc. (see serde)
413            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                // Corresponds to a single lua table
551                quote! { 1 }
552            }
553            Self::Unnamed {
554                field_types: ty, ..
555            } if !ty.is_empty() => {
556                let l = &ctx.as_lua_type_param;
557                // Corresponds to multiple values on the stack (same as tuple)
558                quote! {
559                    <(#(#ty),*) as tlua::LuaRead<#l>>::n_values_expected()
560                }
561            }
562            Self::Unnamed { .. } => {
563                // Unit structs aren't supported yet, but when they are, they'll
564                // probably correspond to a single value
565                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            // These cannot actually appear in struct/enum field types,
798            // but who cares
799            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}