接受 proc 宏属性的多个值

Accept multiple values on proc macro attribute

我希望能够从这样的属性中检索内容:

#[foreign_key(table = "some_table", column = "some_column")]

这就是我正在尝试的方式:

impl TryFrom<&&Attribute> for EntityFieldAnnotation {
    type Error = syn::Error;

    fn try_from(attribute: &&Attribute) -> Result<Self, Self::Error> {
        if attribute.path.is_ident("foreign_key") {
            match attribute.parse_args()? {
                syn::Meta::NameValue(nv) => 
                    println!("NAME VALUE: {:?}, {:?}, {:?}", 
                        nv.path.get_ident(), 
                        nv.eq_token.to_token_stream(),
                        nv.lit.to_token_stream(),
                    ),
                _ => println!("Not interesting")
            }
        } else {
            println!("No foreign key")
        }

    // ... More Rust code
}

如果我只放一个 NameValue 就一切正常。当我添加逗号时, 一切都坏了。

唯一错误: error: unexpected token

我怎样才能修正我的逻辑以实现不止一个的可能性NameValue

谢谢

更新:在写这个答案时,我忘记了 Meta 也有 List 变体,这给了你 NestedMeta。我通常更愿意这样做,而不是我在下面的答案中所做的,以获得更大的灵活性。

虽然,对于您的特定情况,使用 Punctuated 对我来说仍然更简单、更清晰。


MetaNameValue 仅代表一对 name-value。在您的情况下,它由 , 分隔,因此,您需要将所有这些分隔值解析为 MetaNameValue

您可以使用 parse_args_with along with Punctuated::parse_terminated:

而不是调用 parse_args
use syn::{punctuated::Punctuated, MetaNameValue, Token};

let name_values: Punctuated<MetaNameValue, Token![,]> = attribute.parse_args_with(Punctuated::parse_terminated).unwrap(); // handle error instead of unwrap

上面的 name_values 具有类型 Punctuated,它是一个迭代器。您可以遍历它以在您的属性中获得各种 MetaNameValue


根据评论更新

MetaNameValue 中获取 String 的价值:

let name_values: Result<Punctuated<MetaNameValue, Token![,]>, _> = attr.parse_args_with(Punctuated::parse_terminated);

match name_values {
    Ok(name_value) => {
        for nv in name_value {
            println!("Meta NV: {:?}", nv.path.get_ident());
            let value = match nv.lit {
                syn::Lit::Str(v) => v.value(),
                _ => panic!("expeced a string value"), // handle this err and don't panic
            };
            println!( "Meta VALUE: {:?}", value )
        }
    },
    Err(_) => todo!(),
};