kash_macros 0.7.1

Function and method cache and memoization
Documentation
use proc_macro2::{Ident, TokenStream, TokenStream as TokenStream2};
use quote::quote;
use syn::token::Async;
use syn::{GenericArgument, PathArguments, ReturnType, Type};

pub fn gen_init_and_get(
    asyncness: &Option<Async>,
    init_cache_ident: &TokenStream,
    return_cache_block: TokenStream,
    async_cache_get_return: TokenStream,
) -> TokenStream {
    if asyncness.is_some() {
        quote! {
            let kash_cache = #init_cache_ident.get_or_init(kash_init).await;
            #async_cache_get_return
        }
    } else {
        quote! {
            let kash_cache = #init_cache_ident;
            if let Some(kash_result) = kash_cache.get(&kash_key)? {
                #return_cache_block
            }
        }
    }
}

pub fn gen_function_call(
    asyncness: &Option<Async>,
    without_self_names: &[TokenStream],
    call_prefix: TokenStream,
    no_cache_fn_ident: Ident,
) -> TokenStream {
    if asyncness.is_some() {
        quote! {
            let kash_result = #call_prefix #no_cache_fn_ident(#(#without_self_names),*).await;
        }
    } else {
        quote! {
            let kash_result = #call_prefix #no_cache_fn_ident(#(#without_self_names),*);
        }
    }
}

pub fn gen_return_cache_block(result: bool, option: bool) -> TokenStream2 {
    match (result, option) {
        (false, false) => {
            quote! { return Ok(kash_result.to_owned()) }
        }
        (true, false) => {
            quote! { return Ok(kash_result.to_owned()) }
        }
        (false, true) => {
            quote! { return Ok(Some(kash_result.clone())) }
        }
        _ => unreachable!("All errors should be handled in the `MacroArgs` validation methods"),
    }
}

pub fn gen_set_return_block(
    asyncness: &Option<Async>,
    init_cache_ident: TokenStream2,
    function_call: TokenStream2,
    set_cache_and_return: TokenStream2,
) -> TokenStream2 {
    if asyncness.is_some() {
        quote! {
            #function_call
            let kash_cache = #init_cache_ident.get_or_init(kash_init).await;
            #set_cache_and_return
        }
    } else {
        quote! {
            #function_call
            let kash_cache = #init_cache_ident;
            #set_cache_and_return
        }
    }
}

pub fn gen_cache_value_type(result: bool, option: bool, output: &ReturnType) -> TokenStream2 {
    match output {
        ReturnType::Default => panic!("Should return a Result"),
        ReturnType::Type(_, ty) => match (result, option) {
            (true, true) => {
                unreachable!("All errors should be handled in the `MacroArgs` validation methods")
            }
            (false, true) => match output {
                ReturnType::Default => {
                    panic!("Function must return something for `result` or `option` attributes")
                }
                ReturnType::Type(_, ty) => {
                    if let Type::Path(typepath) = *ty.clone() {
                        let segments = typepath.path.segments;
                        if let PathArguments::AngleBracketed(brackets) =
                            &segments.last().unwrap().arguments
                        {
                            let inner_ty = brackets.args.first().unwrap();
                            if let GenericArgument::Type(inner_inner_ty) = inner_ty {
                                if let Type::Path(typepath) = inner_inner_ty.clone() {
                                    let segments = typepath.path.segments;
                                    if let PathArguments::AngleBracketed(brackets) =
                                        &segments.last().unwrap().arguments
                                    {
                                        let inner_ty = brackets.args.first().unwrap();
                                        quote! {#inner_ty}
                                    } else {
                                        panic!(
                                            "Function return type has no inner type, you should remove `result` or `option`"
                                        )
                                    }
                                } else {
                                    panic!("Function return type is too complex")
                                }
                            } else {
                                panic!("Function return type is too complex")
                            }
                        } else {
                            panic!(
                                "Function return type has no inner type, you should remove `result` or `option`"
                            )
                        }
                    } else {
                        panic!("Function return type is too complex")
                    }
                }
            },
            _ => match output {
                ReturnType::Default => quote! {#ty},
                ReturnType::Type(_, ty) => {
                    if let Type::Path(typepath) = *ty.clone() {
                        let segments = typepath.path.segments;
                        if let PathArguments::AngleBracketed(brackets) =
                            &segments.last().unwrap().arguments
                        {
                            let inner_ty = brackets.args.first().unwrap();
                            quote! {#inner_ty}
                        } else {
                            panic!(
                                "Function return type has no inner type, you should remove `result` or `option`"
                            )
                        }
                    } else {
                        panic!("Function return type is too complex")
                    }
                }
            },
        },
    }
}