0

Is there a way to make these two macros the same macro?

macro_rules! bg_color_test {
    ($($color:path, $flag:literal),*$(,)?) => {
        let mut options = Options::default();
        options.text = String::from("my text");

        $(
            options.background = $color;
            assert_eq!(
                parse_args(vec![
                    "path/to/bin".to_string(),
                    "my text".to_string(),
                    "-b".to_string(),
                    $flag.to_string()
                ]),
                options
            );
        )*
    }
}
// used like below:
bg_color_test!(
    Colors::Red,"red",
    Colors::Green,"green",
    Colors::Blue,"blue",
);

And this one:

macro_rules! color_test {
    ($($color:expr, $flag:literal),*$(,)?) => {
        let mut options = Options::default();
        options.text = String::from("my text");

        $(
            options.colors = $color;
            assert_eq!(
                parse_args(vec![
                    "path/to/bin".to_string(),
                    "my text".to_string(),
                    "-c".to_string(),
                    $flag.to_string()
                ]),
                options
            );
        )*
    }
}
// used like below:
color_test!(
    vec![Colors::Red],"red",
    vec![Colors::Green],"green",
    vec![Colors::Blue],"blue",
);

I’m struggling specifically with this bit: options.background = $color; vs options.colors = $color;… no idea how I can build that into the macro…

Within the macro I tried below to make the key dynamic:

options.$kind = $color; // syntax error
options[$kind] = $color; // syntax error
options.$($kind)* = $color; // empty expression error

The way I use the macro is like this:

bg_color_test!(
    "background", Colors::Red,"red",
    "background", Colors::Green,"green",
    "background", Colors::Blue,"blue",
);

Which will give me:

error: no rules expected the token `"background"`
   --> tests/parse_args_test.rs:491:3
    |
464 | macro_rules! bg_color_test {
    | ------------------------ when calling this macro
...
491 |         "background", BgColors::Red, "red",
    |         ^^^^^^^^^^^^ no rules expected this token in macro call

error: could not compile `lib` due to previous error
Dominik
  • 6,078
  • 8
  • 37
  • 61
  • What about taking a single color as argument in `$color`, and then `options.background = $color;` and `options.colors = vec![$color];`? – jthulhu Apr 23 '22 at 09:18
  • The issue is not me passing in two different types here. The issue is that I can't make the key dynamic in `options.background = $color;`. It would have to be something like `options.$kind = $color;` but that doesn't work. – Dominik Apr 23 '22 at 09:23

1 Answers1

0

I found the answer on Discord with the help of user @pie_flavor.

My mistake was to treat the key argument as a str in the macro call, it should be an ident as the argument to begin with.

So the final code of merging those two macros together is:

macro_rules! color_test {
    ($($kind:ident, $color:expr, $flag:literal, $flag_value:literal),*$(,)?) => {
        let mut options = Options::default();
        options.text = String::from("my text");

        $(
            options.$kind = $color;
            assert_eq!(
                parse_args(vec![
                    "path/to/bin".to_string(),
                    "my text".to_string(),
                    $flag.to_string(),
                    $flag_value.to_string()
                ]),
                options
            );
        )*
    }
}

Used like this:

color_test!(
    colors, vec![Colors::Red], "-c", "red",
    background, Colors::Green, "-b", "green",
);
Dominik
  • 6,078
  • 8
  • 37
  • 61