Procedural macro and match. Complicated

Hello,

In a procedural macro, how could we generate a match for a specific enum?


enum Enum{
 A,
 B(u32),
 C
}

and generate

match val{
        0 => /*code for A*/,
        1 => /*code for B*/,
        2 =>/* code for C*/
}

I don't care about the discriminants, just the order and the matching data.
I don't need the boilerplate for the procedural macro, but a hint is welcome.

Is it even possible ?

Thank you.

If you wanted to just generate match for an arbitrary val, then that's not possible. Macros run before types are created. Macros have ability to create and modify definitions of types, so any knowledge of the types would create a circular dependency. Macros operate only on the syntax, just the source code they're invoked on.

You will need to parse the enum definition yourself in the macro, and make assumptions about what are the integer discriminants of the fields, or assign some explicitly when generating the enum definition and code to parse. And then you'll have to generate the integer-to-enum code yourself from the macro.

See existing macros for this:

3 Likes

Thanks,

Actually, for any enum I wanted to be able to generate a corresponding match in a function.

So I have:

fn manage_enum(input: &DeriveInput, data_enum: &DataEnum) -> TokenStream {

        ...
        for (variant_idx, variant) in data_enum.variants.iter().enumerate() {

I can store the names in a vec of TokenStream but I can't generate a macro that generate the corresponding match anyway later on.

The goal is to give a selector and conduct the logic to the proper branch.
When written by hand, the match works. But I can't auto generate it.

I will look into strum,

thank you kornel

Ah that's interesting:
strum does what I am seeking for:

impl Enum {
        #[doc = "Try to create [Self] from the raw representation"]

        fn from_repr(discriminant: u8) -> Option<Enum> {

                #[allow(non_upper_case_globals)]

                const A_DISCRIMINANT: u8 = 0;

                #[allow(non_upper_case_globals)]

                const B_DISCRIMINANT: u8 = A_DISCRIMINANT + 1;

                #[allow(non_upper_case_globals)]

                const C_DISCRIMINANT: u8 = B_DISCRIMINANT + 1;

                #[allow(non_upper_case_globals)]

                const D_DISCRIMINANT: u8 = C_DISCRIMINANT + 1;

                match discriminant {
                        v if v == A_DISCRIMINANT => ::core::option::Option::Some(Enum::A),
                        v if v == B_DISCRIMINANT => ::core::option::Option::Some(Enum::B),
                        v if v == C_DISCRIMINANT => ::core::option::Option::Some(Enum::C),
                        v if v == D_DISCRIMINANT => ::core::option::Option::Some(Enum::D(::core::default::Default::default())),
                        _ => ::core::option::Option::None
                }
        }
}

Whenever I change the enum, it recreates the match.
How does it do it, I mean the match. This is the knowledge I need.

Ok,
the repo put me on the right track.
Specifically:

match discriminant {
                    #(#arms),*
 }

I will duplicate the logic.
Thanks kornel for the hints, you nailed it!