aslip_macro 0.1.11

helper macros for aslip
Documentation
use crate::{AttibuteArgList, FnArgInfo};
use proc_macro::TokenStream;
use quote::quote;
use syn::{ItemFn, spanned::Spanned};

extern crate proc_macro;

// 实例化插件注册表

#[derive(Clone)]
#[allow(dead_code)]
pub struct FnInfo {
    pub func_name: String,
    pub func_args: Vec<FnArgInfo>,

    pub func_doc_comments: Vec<String>,

    /// macro attribute args.
    pub attr_args: AttibuteArgList,

    // span
    pub local_file_path: String,
    pub line: usize,
    pub colum: usize,
}

impl FnInfo {
    pub fn new(attr: TokenStream, fn_item: &ItemFn) -> syn::Result<FnInfo> {
        // 1. 获取函数名称
        let fn_name = &fn_item.sig.ident;

        // 2. 遍历参数,提取参数名称和类型
        let mut func_args_pair = Vec::<FnArgInfo>::new();
        {
            for arg in &fn_item.sig.inputs {
                let f = FnArgInfo::new(arg.clone()).expect(concat!(file!(), line!()));
                func_args_pair.push(f);
            }
        }

        // 3. 从属性中提取文档注释
        let func_doc_comment = FnInfo::get_doc_comments(fn_item.clone());

        let func_span = fn_item.span().unwrap();
        let func_in_path = {
            if let Some(buf) = func_span.local_file() {
                if let Ok(buf_2) = buf.canonicalize() {
                    if let Some(s) = buf_2.to_str() {
                        s.to_string()
                    } else {
                        func_span.file()
                    }
                } else {
                    func_span.file()
                }
            } else {
                func_span.file()
            }
        };

        // 4. parse attribute macro arguments.
        let attribute_arg_list = {
            let re: syn::Result<AttibuteArgList> = syn::parse(attr);
            match re {
                Ok(list) => list,
                Err(e) => {
                    return Err(e);
                }
            }
        };

        let fn_info = FnInfo {
            func_name: quote!(#fn_name).to_string(),
            func_args: func_args_pair,
            func_doc_comments: func_doc_comment,
            local_file_path: func_in_path,
            line: func_span.line(),
            colum: func_span.column(),
            attr_args: attribute_arg_list,
        };

        return Ok(fn_info);
    }

    /// 获取函数的文档注释。
    pub fn get_doc_comments(item_fn: ItemFn) -> Vec<String> {
        item_fn
            .attrs
            .iter()
            .filter_map(|attr| {
                if attr.path().is_ident("doc") {
                    // #[doc = "xxx"]
                    match attr.meta.clone() {
                        syn::Meta::NameValue(nv) => {
                            if let syn::Expr::Lit(expr_lit) = nv.value {
                                if let syn::Lit::Str(litstr) = expr_lit.lit {
                                    return Some(litstr.value());
                                }
                            }
                            None
                        }
                        _ => None,
                    }
                } else {
                    None
                }
            })
            .collect()
    }

    // "one_arg" => {
    //      ...
    // },
    pub fn gen_case_code(&self) -> String {
        // 实参。
        let mut variables: Vec<String> = vec![];

        let mut args_geter: Vec<String> = vec![];
        {
            for index in 0..self.func_args.len() {
                let arg = self.func_args.get(index).expect(concat!(file!(), line!()));

                // re.0 == "__arg_0___converted";
                // re.1 ==
                //      "
                //      let __arg_0__ = app._user_inputed_cmd_args.get(0).unwrap().clone();
                //      let __arg_0___converted: String =
                //          <String as ::aslip::from_arg_sttr::FromArgStr>::from_arg_str(&__arg_0__)
                //              .unwrap()
                //              .clone();
                //      ";
                let re = arg.gen_type_converter_code(index);

                variables.push(re.0);
                args_geter.push(re.1);
            }
        }

        let pattern = {
            match self.attr_args.get("name") {
                Some(arg) => &arg.value,
                None => &self.func_name,
            }
        };

        return format!(
            r###"
            "{pattern}" => 
                    {{
                    
                {args_geter}

                {func_name}({args});
                }}  ,


"###,
            func_name = self.func_name,
            args_geter = args_geter.join("\n"),
            args = variables.join(","),
        );
    }

    // "one_arg" => {
    //      ...
    // },
    pub fn gen_short_name_case_code(&self) -> String {
        // 实参。
        let mut variables: Vec<String> = vec![];

        let mut args_geter: Vec<String> = vec![];
        {
            for index in 0..self.func_args.len() {
                let arg = self.func_args.get(index).expect(concat!(file!(), line!()));

                let re = arg.gen_type_converter_code(index);

                variables.push(re.0);
                args_geter.push(re.1);
            }
        }

        let short_name = {
            match self.attr_args.get("short") {
                Some(arg) => &arg.value,
                None => "",
            }
        };

        return format!(
            r###"
            "{short_name}" => 
                    {{
                    
                {args_geter}

                {func_name}({args});
                }}  ,


"###,
            func_name = self.func_name,
            args_geter = args_geter.join("\n"),
            args = variables.join(","),
        );
    }

    /// 生成 trait bound check 代码, 大概长这样。
    /// const _: () = {
    ///     ::aslip::from_arg_sttr::from_arg_str_trait_bound_check::<Haha>();
    /// };
    pub fn gen_trait_bound_check(&self) -> String {
        if self.func_args.is_empty() {
            return String::new();
        }

        let mut each_check: String = String::new();
        {
            for a in &self.func_args {
                let ty_name = if a.is_colection_type() {
                    &crate::get_inner_ty(&a.type_name)
                } else {
                    &a.type_name
                };

                each_check.push_str(&gen_check_one(ty_name));
                each_check.push_str("\n");
            }
        }

        let trait_bound_check_code = format!(
            r###"

    // trait bound check.
    const _: () = {{
        {each_check}
    }};
    "###,
            each_check = each_check
        );

        fn gen_check_one(type_name: &str) -> String {
            format!(
                "        ::aslip::from_arg_sttr::from_arg_str_trait_bound_check::<{}>();",
                type_name
            )
        }

        return trait_bound_check_code;
    }

    /// 生成 给 app 初始化 _commands 的代码。
    pub fn gen_push_to_app_command_list(&self) -> proc_macro2::TokenStream {
        let name: &str = {
            if let Some(arg) = self.attr_args.get("name") {
                &arg.value
            } else {
                &self.func_name
            }
        };

        let short_name: &str = {
            if let Some(arg) = self.attr_args.get("short") {
                &arg.value
            } else {
                ""
            }
        };

        let about: &str = {
            if let Some(arg) = self.attr_args.get("about") {
                &arg.value
            } else {
                // dbg!(&self.func_doc_comments);
                match self.func_doc_comments.first() {
                    Some(val) => val,
                    None => "",
                }
            }
        };

        let quick_help: &str = &self.func_doc_comments.join("\n");

        let mut args = quote! {};
        {
            for x in &self.func_args {
                let arg_name = &x.arg_name;
                let type_name = &x.type_name;
                args = quote! {
                  #args   (#arg_name, #type_name) ,
                };
            }
            args = quote! { &[ #args ] };
        }
        //           app._commands.insert( "one_arg", ::aslip::app::CmdInfo::new("one_arg", "", "", &[]), );

        return quote! {
            app._commands.push(
                 ::aslip::app::CmdInfo::new(#name, #short_name,    ::aslip::tools::color_print::cstr!(#about) , #args,  ::aslip::tools::color_print::cstr!(#quick_help)  ));
        };
    }
}
impl FnInfo {
    pub(crate) fn gen_key(&self) -> String {
        self.local_file_path.clone() + ":" + &self.func_name
    }
}