Преобразование вариантов перечисления с именованными данными в отдельные структуры с использованием процедурных макросов
Я пишу процедурный макрос, чтобы преобразовать варианты перечисления в отдельные структуры и реализовать некоторые черты для этой структуры.
Это хорошо работает для единичных и безымянных вариантов, но варианты с именованными данными приведут к молчаливому сбою:).
Вот пример определения proc_macro:
extern crate proc_macro;
use quote::ToTokens;
use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DataEnum, DeriveInput};
#[proc_macro_derive(MyProcMacro)]
pub fn derive_my_proc_macro(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
// Error out if we're not annotating an enum
let data: DataEnum = match ast.data {
Data::Enum(d) => d,
_ => panic!("My structs can only be derived for enums"),
};
let variants = data.variants.iter();
let variant_structs = variants.map(|v| {
let var_id = &v.ident;
let fields = v.fields.clone().into_token_stream();
quote! {
pub struct #var_id #fields;
/* Implement traits for the new struct and stuff */
}
});
let gen = quote! {
#(#variant_structs)*
};
gen.into()
}
Когда я запускаю его на этот код:
#[derive(MyProcMacro)]
enum AllTypesOfVariants {
Unit,
OneUnNamed(bool),
MultiUnNamed(bool, bool, bool),
Named { _thing: bool },
MultiNamed { _thing: bool, _thing2: bool },
}
Я получил этот расширенный код (через cargo expand
):
pub struct Unit;
pub struct OneUnNamed(bool);
pub struct MultiUnNamed(bool, bool, bool);
pub struct Named {
_thing: bool,
}
Ожидаемый результат однако будет:
pub struct Unit;
pub struct OneUnNamed(bool);
pub struct MultiUnNamed(bool, bool, bool);
pub struct Named {
_thing: bool,
}
pub struct MultiNamed {
_thing: bool,
_thing2: bool
}
1 ответ
Проблема в точке с запятой в quote!()
,
Структуры с неназванными полями должны заканчиваться точкой с запятой:
pub struct MultiUnNamed(bool, bool, bool);
Но структуры с именованными полями не должны:
pub struct MultiNamed {
_thing: bool,
_thing2: bool
}
Проблема была решена путем замены:
quote! {
pub struct #var_id #fields;
}
с участием
match &v.fields {
Fields::Named(_) => {
quote! {
pub struct #var_id #fields
}
},
_ => {
quote! {
pub struct #var_id #fields;
}
}
}
(Также пришлось импортировать syn::Fields
)