Hi, I'm building a derive macro and was wondering what is the best way to parse a struct/enum attribute that has key value args.
Input
#[derive(Example)]
#[example(a = 5, b = "test")]
struct Struct;
How I'm currently doing it
struct Args {
a: LitInt,
b: LitStr,
}
impl Parse for Args {
fn parse(tokens: ParseStream) -> Result<Self> {
let mut a = None;
let mut b = None;
let parsed = Punctuated::<MetaNameValue, Token![,]>::parse_terminated(tokens)?;
for kv in parsed.clone() {
if kv.path.is_ident("a") {
if a.is_some() {
bail!(kv, "`a` provided twice");
} else if let Lit::Int(v) = kv.lit {
a = Some(v);
} else {
bail!(kv.lit, "`a` value should be an integer");
}
} else if kv.path.is_ident("b") {
if b.is_some() {
bail!(kv, "`b` provided twice");
} else if let Lit::Str(v) = kv.lit {
b = Some(v);
} else {
bail!(kv.lit, "`b` value should be a string");
}
}
}
match (a, b) {
(Some(a), Some(b)) => Ok(Self { a, b }),
(None, None) => bail!(parsed, "missing both `a` and `b`"),
(None, _) => bail!(parsed, "missing `a`"),
(_, None) => bail!(parsed, "missing `b`"),
}
}
}
fn parse_args(input: &DeriveInput) -> Result<Args> {
for attr in &input.attrs {
if attr.path.is_ident("example") {
return attr.parse_args();
}
}
bail!(
input,
"#[derive(Example)] requires an `#[example(..)]` attribute"
);
}
Is there a better way of doing this? I also tried to use syn::AttributeArgs but I couldn't figure out how to get that from attr.tokens.