Reputation: 115
I am building a procedural macro in Rust that implements, for a given struct, the fmt::Display trait for it. Although the implementation does not change the most of the time, there are some cases the subject struct contains some fields that must be displayed in a specific way.
Inside the macro, I did iterate all input fields to determine if the field is present or not. If it does so, I need to add a specific line at the end of the Display implementation. Otherwise, the line must be ignored.
So, I was wondering, is there any way to add conditional code? Just like I show in the example below:
#[proc_macro_derive(DisplayStruct)]
pub fn display_struct(input: TokenStream) -> TokenStream {
let DeriveInput { ident, data, .. } = parse_macro_input!(input);
match data {
Data::Struct(data_struct) => {
let mut has_field = false;
data_struct.fields.iter().for_each(|field| {
if let Some(ident) = field.ident.as_ref() {
has_field |= ident.eq("my_field_name");
}
});
let code = quote! {
impl std::fmt::Display for #ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
// this IF from here is not part of the Display implementation, but a macro conditional that tells to the macro if the contained line should be added or not.
if #has_field {
write!(f, "{}", self.my_field_name.to_string())?;
}
Ok(())
}
}
};
return code.into();
}
_ => {
let error: TokenStream = quote!(compile_error!("chonk UwU");).into();
return error;
}
}
}
So the resulting implementation if my_field_name
is present should be:
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
write!(f, "{}", self.my_field_name.to_string())?;
Ok(())
}
Otherwise it must look like this:
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
Ok(())
}
Upvotes: 2
Views: 1157
Reputation: 70830
Store this line into Option
:
let line = has_field.then(|| {
quote! { write!(f, "{}", self.my_field_name.to_string())?; }
});
Then you can interpolate it into quote!
:
let code = quote! {
impl std::fmt::Display for #ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
#line
Ok(())
}
}
};
Upvotes: 3