alchemist-macros 0.0.0

Hot-reload game engine & editor (WIP—MVP coming)
Documentation
use super::*;
use quote::*;
use syn::*;

pub fn manifest_as(attr: TokenStream, input: TokenStream) -> TokenStream {
    let attr_path = parse_macro_input!(attr as Path);
    let input = parse_macro_input!(input as Item);

    let impl_item = match &input {
        Item::Impl(impl_item) => impl_item,
        _ => return TokenStream::from(quote! {
            ::core::compile_error!("The #[manifest_as] attribute can only be used on trait implementations");
        }),
    };

    let trait_path = match &impl_item.trait_ {
        Some((_, path, _)) => path.clone(),
        None => return TokenStream::from(quote! {
            ::core::compile_error!("The #[manifest_as] attribute can only be used on trait implementations");
        }),
    };

    let core_path = utils::core_path_attr(&impl_item.attrs);

    let assert_trait_equality = assert_trait_equality(&attr_path, &trait_path);

    let implementing_type = &impl_item.self_ty;

    let registration_implementation = quote! {
        #core_path::inventory::submit! {
            #core_path::manifest::ManifestableRegistration::<Box<dyn #trait_path>> {
                type_id_fn: || ::std::any::TypeId::of::<#implementing_type>(),
                qualifier_fn: || {
                    use #core_path::manifest::Qualify;
                    #implementing_type::qualifier()
                },
                default_fn: || {
                    use ::std::default::Default;
                    Box::new(#implementing_type::default())
                },
                encode_fn: |v, encoder| {
                    let force_downcasted: &#implementing_type = unsafe {
                        let (data, _vtable): (*const (), *const ()) = ::std::mem::transmute(v.as_ref());
                        ::std::mem::transmute(data)
                    };
                    encoder.delegate_encoding(force_downcasted)
                },
                decode_fn: |v, decoder| -> Result<(), Box<dyn ::std::error::Error>> {
                    let force_downcasted: &mut #implementing_type = unsafe {
                        let (data, _vtable): (*const (), *const ()) = ::std::mem::transmute(v.as_ref());
                        ::std::mem::transmute(data)
                    };
                    decoder.delegate_decoding(force_downcasted)?;
                    Ok(())
                },
                encompass_fn: |v, visitor| {
                    let force_downcasted: &#implementing_type = unsafe {
                        let (data, _vtable): (*const (), *const ()) = ::std::mem::transmute(v.as_ref());
                        ::std::mem::transmute(data)
                    };
                    use #core_path::encompass::Encompass;
                    force_downcasted.encompass(visitor)
                },
                inspect_fn: |v, ui_context, ui| {
                    let force_downcasted: &mut #implementing_type = unsafe {
                        let (data, _vtable): (*const (), *const ()) = ::std::mem::transmute(v.as_ref());
                        ::std::mem::transmute(data)
                    };
                    use #core_path::inspect::Inspect;
                    force_downcasted.inspect(ui_context, ui);
                },
                debug_fn: |v, formatter| {
                    let force_downcasted: &#implementing_type = unsafe {
                        let (data, _vtable): (*const (), *const ()) = ::std::mem::transmute(v.as_ref());
                        ::std::mem::transmute(data)
                    };
                    use ::std::fmt::Debug;
                    force_downcasted.fmt(formatter)
                },
            }
        }
    };

    let full_implementation = quote! {
        #impl_item
        #assert_trait_equality
        #registration_implementation
    };

    full_implementation.into()
}

pub fn assert_trait_equality(first: &Path, second: &Path) -> TokenStream2 {
    quote! {
        const _: () = {
            use std::marker::PhantomData;
            pub struct AssertSameTrait<T: ?Sized> {
                phantom: PhantomData<T>,
            }
            let _: AssertSameTrait<dyn #first> = AssertSameTrait::<dyn #second> { phantom: PhantomData };
        };
    }
}