next-web-macros 0.1.0

Provide a series of commonly used macros
Documentation
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{parse_quote, spanned::Spanned, Error, FnArg, Ident, ItemFn, Type};

pub fn impl_macro_find_singleton(_attr: TokenStream, item: ItemFn) -> TokenStream {
    generate(item).unwrap_or_else(|e| e.to_compile_error().into())
}

fn generate(mut item: ItemFn) -> Result<TokenStream, Error> {
    let vis = &item.vis;

    if let None = item.sig.asyncness {
        return Err(Error::new(
            item.sig.fn_token.span(),
            "The function must be declared as async",
        ));
    };

    let inputs = &item.sig.inputs;
    if inputs.iter().any(|val| match val {
        FnArg::Receiver(_) => true,
        _ => false,
    }) {
        return Err(Error::new(
            inputs.span(),
            "The function must not have a receiver, self",
        ));
    }

    let mut streams = Vec::new();
    for (index, fn_arg) in item.sig.inputs.iter_mut().enumerate() {
        match fn_arg {
            FnArg::Receiver(_) => {}
            FnArg::Typed(pat_type) => {

                // println!("pate: {:#?}", pat_type);
                match pat_type.ty.as_mut() {
                    syn::Type::Path(type_path) => {

                        let is_single = type_path.path.segments.iter().any(|seg| seg.ident.to_string().contains("FindSingleton"));
                        for seg in type_path.path.segments.iter_mut() {
                            if seg.ident.to_string().contains("FindSingleton")
                                && match &seg.arguments {
                                    syn::PathArguments::AngleBracketed(_) => true,
                                    _ => false,
                                }
                            {
                                match &mut seg.arguments {
                                    syn::PathArguments::AngleBracketed(angle_bracketed) => {
                                        let mut  flag = false;
                                        if let Some(syn::GenericArgument::Type(typ)) =
                                            angle_bracketed.args.first()
                                        {
                                            if let Type::Path(type_path) = typ {
                                                

                                                let original_variable = match pat_type.pat.as_ref() {
                                                    syn::Pat::Ident(pat_ident) => {
                                                        pat_ident.ident.clone()
                                                    }
                                                    syn::Pat::TupleStruct(pat_tuple_struct) => {
                                                        if let Some(syn::Pat::Ident(pat_ident)) =
                                                            pat_tuple_struct.elems.first()
                                                        {
                                                            pat_ident.ident.clone()
                                                        } else {
                                                            return Err(Error::new(
                                                                pat_type.pat.span(),
                                                                 "The pattern must be an identifier or a tuple struct with one identifier"));
                                                        }
                                                    }
                                                    _ => return Err(Error::new(

                                                                 pat_type.pat.span(),
                                                                 "The pattern must be an identifier or a tuple struct with one identifier",
                                                    ))
                                                };

                                                let arg = Type::Path(type_path.clone());

                                                let variable = Ident::new(& format!("_my_state{}", index), Span::call_site());
                                                let single_name = Ident::new(&crate::util::single::field_name_to_singleton_name(&original_variable.to_string()), Span::call_site());
                                                let stream = quote! {
                                                    let #original_variable = #variable.get_single_with_name::<#arg>(stringify!(#single_name)).await;
                                                };

                                                streams.push(stream);

                                                flag = true;
                                            }
                                        }

                                        if flag {

                                            angle_bracketed.args.clear();
                                            angle_bracketed.args.push(syn::GenericArgument::Type(
                                                    parse_quote!(::next_web_core::state::application_state::ApplicationState)
                                            ));
                                            
                                            let pat = syn::Pat::Ident(
                                                syn::PatIdent { attrs: vec![], by_ref: None, mutability: None, ident: 
                                                    Ident::new(format!("_my_state{}", index).as_str(), Span::call_site()), subpat: None
                                                }
                                            );
                                            pat_type.pat = Box::new(pat);
                                        }
                                    }

                                    _ => {}
                                }
                            }
                        }

                        if is_single {
                            let arg: syn::Type  = parse_quote!(::next_web_core::state::application_state::ApplicationState);
                            let path: syn::Path = parse_quote!(::axum::Extension<#arg>);
                            type_path.path = path;
                        }
                    }
                    _ => {}
                };
            }
        }
    }

    let sig = &item.sig;
    let block = &item.block.stmts;

    let token_stream = quote! {
        #vis #sig 
        {
            #(#streams)*

            #(#block)*
        }
    };

    // println!("token_stream: \n{}", token_stream.to_string());

    Ok(token_stream.into())
}