gluon_codegen 0.9.2

Code generation macros for the gluon programming language
Documentation
use proc_macro2::{Ident, Span, TokenStream};
use shared::{map_lifetimes, map_type_params, split_for_impl};
use syn::{self, Data, DeriveInput, GenericParam, Generics};

use attr::{Container, CrateName};

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 + 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;
        },
        CrateName::GluonVm => quote!{
            use api as _gluon_api;
            use thread as _gluon_gc;
        },
        CrateName::None => quote!{
            use gluon::vm::api as _gluon_api;
            use gluon::vm::gc as _gluon_gc;
        },
    };

    let associated_type_generics = generics.params.iter().map(|param| match param {
        GenericParam::Type(ty) => quote!( #ty :: Type ),
        GenericParam::Lifetime(_) => quote!( 'static ),
        GenericParam::Const(c) => quote!( #c ),
    });

    let dummy_const = Ident::new(&format!("_IMPL_USERDATA_FOR_{}", ident), Span::call_site());

    quote! {
        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),*
            {
            }

            #[automatically_derived]
            #[allow(unused_attributes, unused_variables)]
            impl #impl_generics _gluon_gc::Traverseable for #ident #ty_generics {}

            #[automatically_derived]
            #[allow(unused_attributes, unused_variables)]
            impl #impl_generics _gluon_api::VmType for #ident #ty_generics
            #where_clause #(#trait_bounds,)* #(#lifetime_bounds),*
            {
                type Type = #ident<
                        #(#associated_type_generics),*
                    >;
            }
        };
    }
}