0

我有一个 proc_macro,它产生一个遥测函数来解析结构成员变量,它对非嵌套结构非常有用。我发现我需要在任何作为结构的 syn::Field 上递归调用我的 handle_named_field 函数。问题是我看不到确定字段是否为结构的方法,如果我有一个 syn::Data 变量,它是微不足道的,就像在我的 handle_data 中一样。如果字段是否为结构,我如何检查 handle_named_fields(fields: &syn::FieldsNamed) 内部?

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, parse_quote, DeriveInput};

#[proc_macro_derive(Telemetry)]
pub fn derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let output = parse_derive_input(&input);
    match output {
        syn::Result::Ok(tt) => tt,
        syn::Result::Err(err) => err.to_compile_error(),
    }
    .into()
}

fn parse_derive_input(input: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {

    let struct_ident = &input.ident;
    let struct_data = parse_data(&input.data)?;
    let struct_fields = &struct_data.fields;

    let _struct_ident_str = format!("{}", struct_ident);
    let tele_body = match struct_fields {
            syn::Fields::Named(fields_named) => handle_named_fields(fields_named)?,
            syn::Fields::Unnamed(fields_unnamed) => {
                let field_indexes = (0..fields_unnamed.unnamed.len()).map(syn::Index::from);
                let field_indexes_str = (0..fields_unnamed.unnamed.len()).map(|idx| format!("{}", idx));
                quote!(#( .field(#field_indexes_str, &self.#field_indexes) )*)
            }
            syn::Fields::Unit => quote!(),
        };

    let telemetry_declaration = quote!(
        trait Telemetry {
            fn telemetry(self, header_stream: Arc<Mutex::<Box <std::io::Write + std::marker::Send + Sync>>>, data_stream: Arc<Mutex::<Box <std::io::Write + std::marker::Send + Sync>>>);
        }
    );



    syn::Result::Ok(
        quote!(
            use std::thread;
            use std::collections::VecDeque;


            #[derive(Serialize, Deserialize, Default, Debug)]
            pub struct VariableDescription {
                pub var_name_length: usize,
                pub var_name: String,
                pub var_type_length: usize,
                pub var_type: String,
                pub var_size: usize,
        }

        #[derive(Serialize, Deserialize, Default, Debug)]
        pub struct TelemetryHeader {
            pub variable_descriptions: VecDeque::<VariableDescription>,
        }


        #telemetry_declaration
        impl Telemetry for #struct_ident {
            fn telemetry(self, header_stream: Arc<Mutex::<Box <std::io::Write + std::marker::Send + Sync>>>, data_stream: Arc<Mutex::<Box <std::io::Write + std::marker::Send + Sync>>>) {
                        thread::spawn(move || {
                             #tele_body;
                        });
            }
        }
    )
    )
}

fn handle_named_fields(fields: &syn::FieldsNamed) -> syn::Result<proc_macro2::TokenStream> {
    let idents = fields.named.iter().map(|f| &f.ident);
    let types = fields.named.iter().map(|f| &f.ty);
    let num_entities = fields.named.len();
    let test = quote! (
                let mut tele_header = TelemetryHeader {variable_descriptions: VecDeque::with_capacity(#num_entities)};
                #(
                    if()
                        tele_header.variable_descriptions.push_back( VariableDescription {
                        var_name_length: stringify!(#idents).len(),
                        var_name: stringify!(#idents).to_string(),
                        var_type_length: stringify!(#types).len(),
                        var_type: stringify!(#types).to_string(),
                        var_size: std::mem::size_of_val(&self.#idents),
                    });

                )*
                header_stream.lock().unwrap().write(&bincode::serialize(&tele_header).unwrap()).unwrap();
                data_stream.lock().unwrap().write(&bincode::serialize(&self).unwrap()).unwrap();
        );
        syn::Result::Ok(test)
}

fn parse_named_field(field: &syn::Field) -> proc_macro2::TokenStream {
    let ident = field.ident.as_ref().unwrap();
    let ident_str = format!("{}", ident);
    let ident_type = &field.ty;
    if field.attrs.is_empty() {

                quote!(
                    if true {
                        println!("TRUE");
                    }
                println!("Var Name Length: {}", stringify!(#ident_str).len());
                println!("Var Name: {}", #ident_str);
                println!("Var Type Length: {}", stringify!(#ident_type).len());
                println!("Var Type: {}", stringify!(#ident_type));
                println!("Var Val: {}", &self.#ident);
            )
    }
    else {
        quote!()
    }
}

fn parse_data(data: &syn::Data) -> syn::Result<&syn::DataStruct> {
    match data {
        syn::Data::Struct(data_struct) => syn::Result::Ok(data_struct),
        syn::Data::Enum(syn::DataEnum { enum_token, .. }) => syn::Result::Err(
            syn::Error::new_spanned(enum_token, "CustomDebug is not implemented for enums"),
        ),
        syn::Data::Union(syn::DataUnion { union_token, .. }) => syn::Result::Err(
            syn::Error::new_spanned(union_token, "CustomDebug is not implemented for unions"),
        ),
    }
}

4

0 回答 0