I am trying to create a simple map macro that should be called like this:
let c = "asdads";
map![
a: "b",
c, // auto expand, same as `c: "asdads"`
d: 345
]
and return a crate::Map(&[(String, String)]).
I have wrapped it in a struct to implement some properties like Display or Debug, and I am using a reference to a slice because a slice is not Sized.
I can write a macro that parses only the full form (a: "b") or the short form (just a) but I can't think of one that will be able to match both.
The one matching the short form:
($($key:ident),*) => {
crate::Map(&[
$((stringify!($key).to_string(), $key.to_string())),*
][..])
};
The one matching the full form:
($($key:ident : $value:expr),*) => {
crate::Map(&[
$((stringify!($key).to_string(), $value.to_string())),*
][..])
};
What I have tried: making two separate macros that match only one line and then passing a tt to it.
macro_rules! map_one {
($key:ident) => {
(stringify!($key).to_string(), $key.to_string())
};
($key:ident : $value:expr) => {
(stringify!($key).to_string(), $value.to_string())
};
}
macro_rules! map {
($($tt:tt),*) => {
crate::Map(&[
$(map_one!($tt)),*
][..])
};
}
I get a compile error:
no rules expected the token `:`
while trying to match `,`.
The
ttmatcher matches a token tree, which is either a single token (ident, operator,:, etc) or a set of tokens grouped within a pair of parenthesis, square brackets, or curly braces.So
key: "value"is actually threetts. The way to work around this is to match optionally on the: $value:exprpart with a$()?group, them pass the whole thing along to yourmap_onemacro:Playground
I would also recommend merging the two macros using the
@prefixpattern:Playground