vine-macros 0.1.4

Vine Macros contains macros
Documentation
use proc_macro::TokenStream;

use proc_macro2::{Ident, Span};
use quote::quote;
use syn::{FnArg, GenericArgument, ItemFn, LitStr, parse_macro_input, parse_quote, PathArguments, PatType, ReturnType, Signature, Type};
use crate::inject::bean_field::BeanField;

pub fn generate_setup_fn_for_bean(_attr: TokenStream, input: TokenStream) -> TokenStream {
    let ItemFn {
        vis,
        sig: Signature { output, ident, inputs, .. },
        block,
        ..
    } = parse_macro_input!(input as ItemFn);

    let args: Vec<_> = inputs.iter().map(|arg| {
        let FnArg::Typed(PatType { pat, ty, .. }) = arg else { panic!("unsupported FnArg"); };
        return quote!(#pat: #ty)
    }).collect();

    let resolved_fn_args: Vec<_> = inputs.iter().map(|fn_arg| match parse_quote!(#fn_arg) {
        BeanField::Bean(_, ty, name) => quote!(ctx.get_bean::<#ty>(#name)?,),
        BeanField::PrimaryBean(_, ty) => quote!(ctx.get_primary_bean::<#ty>()?,),
        BeanField::Beans(_, ty) => quote!(ctx.get_beans::<#ty>()?,),
        BeanField::Value(_, _ty, value) => quote!({
            let config = ctx.get_bean::<dyn vine::vine_core::config::PropertyResolver + Send + Sync>("config")?;
            config.compute_template_value(#value)?
        },),
    }).collect();

    let fn_name_str = ident.to_string();
    let bean_name = LitStr::new(&fn_name_str, Span::call_site());

    let setup_ident = format!("SETUP_{}", fn_name_str.to_uppercase());
    let setup_ident = Ident::new(&setup_ident, Span::call_site());

    let ty = get_create_fn_output(&output);
    let extended = quote!(
        #[vine::distributed_slice(vine::vine_core::context::auto_register_context::SETUP)]
        pub static #setup_ident: fn(&vine::vine_core::context::context::Context) -> Result<(), vine::vine_core::core::Error> = |ctx| {
            let ty = vine::vine_core::core::ty::Type::of::<#ty>();
            ty.add_downcast::<#ty>(|b| Ok(std::sync::Arc::downcast::<#ty>(b)?));

            let bean_def = vine::vine_core::core::bean_def::BeanDef::builder()
                .name(#bean_name)
                .ty(ty)
                .get(std::sync::Arc::new(|ctx| Ok(#ident(#(#resolved_fn_args)*))))
                .build();
            ctx.register(bean_def)
        };

        #vis fn #ident (#(#args),*) #output { #block }
    );

    extended.into()
}

fn get_create_fn_output(return_type: &ReturnType) -> Type {
    let ReturnType::Type(_, box_type) = return_type else { panic!() };
    let Type::Path(path_type) =  box_type.as_ref() else { panic!() };
    let Some(path_seg) = path_type.path.segments.first() else { panic!() };

    let PathArguments::AngleBracketed(x) = &path_seg.arguments else { panic!() };
    let Some(GenericArgument::Type(class)) = x.args.first() else { panic!() };

    class.clone()
}