orz 1.0.0

A procedural macro for generating field information methods for structs.
Documentation
// src/lib.rs
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Data, Fields, Type, Ident};

#[proc_macro_derive(Orz, attributes(name, skip, debug, to_string_with))]
pub fn derive_field_values(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let struct_name = &input.ident;

    let fields = match &input.data {
        Data::Struct(data) => match &data.fields {
            syn::Fields::Named(named) => &named.named,
            _ => {
                return syn::Error::new_spanned(
                    &input,
                    "FieldValues 只支持具名字段结构体",
                )
                .to_compile_error()
                .into();
            }
        },
        _ => {
            return syn::Error::new_spanned(&input, "FieldValues 只支持结构体")
                .to_compile_error()
                .into();
        }
    };

    let mut field_names_lit = Vec::new();
    let mut value_exprs = Vec::new();
    let mut ref_exprs = Vec::new();
    let mut all_asref_str = true;

    for field in fields {
        let field_name = &field.ident;
        let field_type = &field.ty;

        let mut skip = false;
        let mut custom_func = None;
        let mut display_name = None;
        let mut use_debug = false;

        // 解析独立属性
        for attr in &field.attrs {
            if attr.path().is_ident("name") {
                // 只支持 #[name("值")] 格式
                if let Ok(lit_str) = attr.parse_args::<syn::LitStr>() {
                    display_name = Some(lit_str.value());
                } else {
                    return syn::Error::new_spanned(
                        attr,
                        "name attribute must be in the format #[name(\"value\")]"
                    )
                    .to_compile_error()
                    .into();
                }
            } else if attr.path().is_ident("skip") {
                skip = true;
            } else if attr.path().is_ident("debug") {
                use_debug = true;
            } else if attr.path().is_ident("to_string_with") {
                // 只支持 #[to_string_with("函数名")] 格式
                if let Ok(lit_str) = attr.parse_args::<syn::LitStr>() {
                    custom_func = Some(lit_str.value());
                } else {
                    return syn::Error::new_spanned(
                        attr,
                        "to_string_with attribute must be in the format #[to_string_with(\"function\")]"
                    )
                    .to_compile_error()
                    .into();
                }
            }
        }

        if skip {
            continue;
        }

        // 设置字段显示名称
        let field_display_name = if let Some(name) = display_name {
            name
        } else {
            field_name.as_ref().unwrap().to_string()
        };
        field_names_lit.push(field_display_name);

        // 构建字段值表达式
        let value_expr = if let Some(ref func_name) = custom_func {
            let func_ident: Ident = syn::parse_str(func_name).unwrap();
            quote! { #func_ident(&self.#field_name) }
        } else if use_debug {
            quote! { format!("{:?}", &self.#field_name) }
        } else {
            quote! { self.#field_name.to_string() }
        };

        value_exprs.push(value_expr);

        // 检查是否可 AsRef<str>
        if !is_type_asref_str(field_type) {
            all_asref_str = false;
        } else {
            ref_exprs.push(quote! { self.#field_name.as_ref() });
        }
    }

    // 将字段名转换为字面量 token streams
    let field_names_tokens: Vec<_> = field_names_lit.iter()
        .map(|name| {
            let name_lit = syn::LitStr::new(name, proc_macro2::Span::call_site());
            quote! { #name_lit }
        })
        .collect();

    let field_count = field_names_lit.len();

    let expanded = quote! {
        impl #struct_name {
            /// 返回结构体所有字段的显示名称
            pub fn field_names() -> Vec<&'static str> {
                vec![#(#field_names_tokens),*]
            }

            /// 返回结构体所有字段值的 `Vec<String>`
            pub fn field_values(&self) -> Vec<String> {
                vec![#(#value_exprs),*]
            }

            /// 返回字段名和字段值的映射
            pub fn field_info(&self) -> Vec<(&'static str, String)> {
                let names = Self::field_names();
                let values = self.field_values();
                names.into_iter().zip(values.into_iter()).collect()
            }

            /// 返回字段数量
            pub fn field_count() -> usize {
                #field_count
            }
        }
    };

    // 条件生成 field_values_ref()
    let expanded = if all_asref_str && !ref_exprs.is_empty() {
        quote! {
            #expanded
            
            impl #struct_name {
                /// 返回结构体所有字段值的 `Vec<&str>`
                pub fn field_values_ref(&self) -> Vec<&str> {
                    vec![#(#ref_exprs),*]
                }
            }
        }
    } else {
        expanded
    };

    TokenStream::from(expanded)
}

fn is_type_asref_str(ty: &Type) -> bool {
    match ty {
        syn::Type::Reference(t) => {
            if let syn::Type::Path(p) = &*t.elem {
                if p.path.segments.last().map(|s| s.ident == "str").unwrap_or(false) {
                    return true;
                }
            }
        }
        syn::Type::Path(p) => {
            if p.path.segments.last().map(|s| s.ident == "String").unwrap_or(false) {
                return true;
            }
        }
        _ => {}
    }
    false
}