I'm writing a procedural macro attribute and want to add some configuration to it. It will consist of two parts, both of them optional:
- a flag added by simply stating its name,
- a string (in fact, identifier) parameter, added as
prop = ident
part.
For now, I'm able to parse this if there are no config parameters, if there is only one of them, or if thy are set in particular order. But I'd like to make them also acceptable in reverse order, and that's where I'm blocked.
The code in question looks like following:
use syn::{
parse::{Parse, ParseStream},
Result, Token,
};
struct RawConfigFlagFirst {
_flag: Flag,
name: Option<Name>,
}
struct RawConfigNameFirst {
name: Name,
_flag: Option<Flag>,
}
struct Flag;
struct Name(String);
enum RawConfig {
FlagFirst(RawConfigFlagFirst),
NameFirst(RawConfigNameFirst),
}
impl Parse for Flag {
fn parse(stream: ParseStream) -> Result<Self> {
let ident: syn::Ident = stream.parse()?;
if ident.to_string().as_str() == "flag" {
Ok(Flag)
} else {
Err(stream.error("Unexpected token"))
}
}
}
impl Parse for Name {
fn parse(stream: ParseStream) -> Result<Self> {
let ident: syn::Ident = stream.parse()?;
if ident.to_string().as_str() == "name" {
let _: Token![=] = stream.parse()?;
let name: syn::Ident = stream.parse()?;
Ok(Name(name.to_string()))
} else {
Err(stream.error("Unexpected token"))
}
}
}
impl Parse for RawConfigFlagFirst {
fn parse(stream: ParseStream) -> Result<Self> {
Ok(RawConfigFlagFirst {
_allow_self_sized: stream.parse()?,
name: if stream.is_empty() {
None
} else {
let _: Token![,] = stream.parse()?;
Some(stream.parse()?)
},
})
}
}
impl Parse for RawConfigNameFirst {
fn parse(stream: ParseStream) -> Result<Self> {
Ok(RawConfigNameFirst {
name: stream.parse()?,
allow_self_sized: if stream.is_empty() {
None
} else {
let _: Token![,] = stream.parse()?;
Some(stream.parse()?)
},
})
}
}
impl Parse for RawConfig {
fn parse(stream: ParseStream) -> Result<Self> {
let streaf = stream.fork();
match (
streaf.parse::<RawConfigFlagFirst>(),
stream.parse::<RawConfigNameFirst>(),
) {
(Ok(cfg), _) => Ok(RawConfig::FlagFirst(cfg)),
(Err(_), Ok(cfg)) => Ok(RawConfig::NameFirst(cfg)),
(Err(mut err1), Err(err2)) => {
err1.combine(err2);
Err(err1)
}
}
}
}
Then, if I try to parse the string flag, name = name
, I get the Unexpected token
error. Looks like that this is due to the fact that stream
in RawConfig
parsing function is not exhausted, since it tries to parse the wrong structure. Am I doing this somehow systematically wrong, or just don't see some solution in this approach?