use proc_macro2::{Ident, Span, TokenStream}; use syn::{self, Data, DeriveInput, Generics}; use crate::{ attr::{Container, CrateName}, shared::{map_lifetimes, map_type_params, split_for_impl}, }; pub fn derive(input: TokenStream) -> TokenStream { let derive_input = syn::parse2(input).expect("Input is checked by rustc"); let container = Container::from_ast(&derive_input); let DeriveInput { ident, data, generics, .. } = derive_input; let tokens = match data { Data::Struct(_) | Data::Enum(_) => gen_impl(&container, ident, generics), Data::Union(_) => panic!("Unions are not supported"), }; tokens.into() } fn gen_impl(container: &Container, ident: Ident, generics: Generics) -> TokenStream { let trait_bounds = &map_type_params(&generics, |ty| { quote! { #ty: 'static + ::std::fmt::Debug + _gluon_gc::Trace + Sync + Send } }); let lifetime_bounds = &map_lifetimes(&generics, |lifetime| quote! { #lifetime: 'static }); let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &[], &[]); let gluon = match container.crate_name { CrateName::Some(ref ident) => quote! { use #ident::api as _gluon_api; use #ident::gc as _gluon_gc; use #ident::Result as _gluon_Result; }, CrateName::GluonVm => quote! { use crate::api as _gluon_api; use crate::thread as _gluon_gc; use crate::Result as _gluon_Result; }, CrateName::None => quote! { use gluon::vm::api as _gluon_api; use gluon::vm::gc as _gluon_gc; use gluon::vm::Result as _gluon_Result; }, }; let dummy_const = Ident::new(&format!("_IMPL_USERDATA_FOR_{}", ident), Span::call_site()); let deep_clone = if container.clone { quote! { fn deep_clone<'gc>( &self, deep_cloner: &'gc mut _gluon_api::Cloner ) -> _gluon_Result<_gluon_gc::GcRef<'gc, Box>> { let data: Box = Box::new(self.clone()); deep_cloner.gc().alloc(_gluon_gc::Move(data)) } } } else { quote! {} }; quote! { #[allow(non_upper_case_globals)] const #dummy_const: () = { #gluon #[automatically_derived] #[allow(unused_attributes, unused_variables)] impl #impl_generics _gluon_api::Userdata for #ident #ty_generics #where_clause #(#trait_bounds,)* #(#lifetime_bounds),* { #deep_clone } }; } }