LucioleMaléfique
LucioleMaléfique

Reputation: 760

using enum fields in derive macro

Trying to make my own Serialize trait and derive macro, I'm stuck when I need to access the fields of an enum.

For example, on the enum variant :

enum MyEnum {
    MyVariant(f32, u16, u16)
}

I would like to do :

fn Serialize(self) -> Vec<u8> {
    match self {
        MyEnum::MyVariant(field0, field1, field2) => {
            let mut result = Vec::<u8>::new();
            result.append(&mut field0.Serialize());
            result.append(&mut field1.Serialize());
            result.append(&mut field1.Serialize());
            result
        }
    }
}

The idea is that I'll implement the Serialize trait on basic data types, and it then can be extended to various structs and enums.

But I don't know how to create variable names for the fields (the field0, field1, field2) in a derive macro, and then use them.

This is what I'm up to so far :

// create the fields argument tokens (this is "field0, field1, field2")
let mut fields_name = TokenStream2::new();
// operation of fields (this is result.append(field0.Serialize(); [...])
let mut fields_serialization = TokenStream2::new();
// counter to create numbered names for fields
let mut counter = 0;
for _field in &fields.unnamed { // fields is of type &FieldsUnnamed here
    if counter == 0 {
        fields_name.extend(quote!{
            format!("field{}", #counter)
        })
    }
    else {
        fields_name.extend(quote!{
            , format!("field{}", #counter)
        })
    }
    
    fields_serialization.extend(quote_spanned! {variant.span()=>
        result.append(&mut format!("field{}", #counter).Serialize());
    });

    counter += 1;
}

quote_spanned! {variant.span()=>
    #name::#variant_name (#fields_name) => {
        let mut result = Vec::new();
        #fields_serialization
        result
    },
}

I understand that with the format macro, I'll end up with a string token and it doesn't work. But I have no idea how to get all my enum variants fields and operate on them, as I found very little information about it online.

Upvotes: 2

Views: 995

Answers (1)

PitaJ
PitaJ

Reputation: 15012

I'd recommend using .enumerate() and utilizing the repetition features of quote:

let mut field_name: Vec<_> = fields.unnamed.iter().enumerate().map(|(i, _)| {
    format_ident!("field{}", i)
}).collect();

quote_spanned! {variant.span()=>
    #name::#variant_name (#(#field_name,)*) => {
        let mut result = Vec::new();

        #(
            result.append(&mut #field_name.Serialize());
        )*

        result
    },
}

BTW, in Rust, the standard style is for only types to be capitalized. All methods should be lower_snake_case.

Upvotes: 1

Related Questions