Mismatched macro designator

The Problem statement:

GIve a ty: &str and matching it to an Enum, how do I ensure I didn't miss a match arm if I am adding a new variant in GraphqlNode

// the `Model`s cannot implement Default
// and thus cannot use strum::EnumIter
#[derive(async_graphql::Interface)]
enum Node {
   Brand(brand::Model),
   Category(category::Model),
   // Add a new varaiant
   // New(new::Model)   
}

fn work(ty: &str, id: i64) {
   // match the `ty` to a variant
   match ty {
     "Brand" => brand::Model.fetch(id),
     "Category" => category::Model.fetch(id),
     // also remember to add the new type here too
     // "New" => new::Model.fetch(id),
     _ => panic!("unknown type"),
   }
}

The Solution

I asked for help on Discord and this is the approach I am working on. Using the graphql_node macro rule to ensure the match.

mod brand {
    pub struct Model;
}

macro_rules! graphql_node {
    {
        $(($variant_name:ident, $p:path),)*
    } => {
        use async_graphql::Interface;

        #[derive(Interface)]
        enum Node {
            $($variant_name($p),)*
            // the above is expand to below
            // Brand(brand::Model),
        }

        fn work(ty: &str, id: i64) {
            $(const $variant_name: &str = stringify!($variant_name);)*
            match ty {
                // Now I won't miss any match
                $($variant_name => $module::Model.fetch(id),)*
                _ => panic!("unknown type"),
            }
        }
    }
}

graphql_node! {
    (Brand, brand::Model),
}

The Error

I thought I am using the right designator, $p:path, in my macro_rule and it turned out async_graphql is not happy about it on this line because it is expecting a Type::Path(p)

error: Invalid type
  --> src/main.rs:13:29
   |
13 |               $($variant_name($p),)*
   |                               ^^

Naively, I tried to print out $ty in async_graphql and see what type it actually was.

[async-graphql/derive/src/interface.rs:97:9] &ty = Type::Group {
    group_token: Group,
    elem: Type::Path {
        qself: None,
        path: Path {
            leading_colon: None,
            segments: [
                PathSegment {
                    ident: Ident {
                        ident: "brand",
                        span: #0 bytes(378..383),
                    },
                    arguments: PathArguments::None,
                },
                PathSep,
                PathSegment {
                    ident: Ident {
                        ident: "Model",
                        span: #0 bytes(385..390),
                    },
                    arguments: PathArguments::None,
                },
            ],
        },
    },
}

And at this moment I am totally lost as I am not that confident in writing/reading macros.

Questions

  1. Is there a way to print out the fully qualiified name of a type? I couldn't find what Type::Group was, I checked syn, quote and proc_macro2 and couldn't find an exact match.
  2. I specified $p:path but when it reached to async_graphql, it became a Type::Group? How do I fix it?

You can still use strum for this purpose.

use strum::{EnumDiscriminants, EnumString};

#[derive(EnumDiscriminants)]
#[strum_discriminants(derive(EnumString))]
enum Node {
    Brand(brand::Model),
    Category(category::Model),
    // Add a new varaiant
    // New(new_model::Model)
}

fn work(ty: &str, id: i64) {
    use NodeDiscriminants as Ty;
    let ty = ty.parse::<Ty>().unwrap_or_else(|_| panic!("unknown type"));

    // match the `ty` to a variant
    match ty {
        Ty::Brand => {}
        Ty::Category => {}
    }
}

(Sorry for not actually answering your questions.)

1 Like