構造体のメンバー変数を解析するテレメトリ関数を生成する 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"),
),
}
}