如何在Rust中以编程方式读取属性

时间:2019-02-07 12:18:11

标签: rust

我想以编程方式阅读attributes。例如,我有一个具有附加到每个字段的属性的结构:

#[derive(Clone, Debug, PartialEq, Message)]
pub struct Person {
    #[prost(string, tag="1")]
    pub name: String,
    /// Unique ID number for this person.
    #[prost(int32, tag="2")]
    pub id: i32,
    #[prost(string, tag="3")]
    pub email: String,
    #[prost(message, repeated, tag="4")]
    pub phones: Vec<person::PhoneNumber>,
}

source

我想找到与电子邮件字段关联的标签。

我希望有一些类似的代码可以在运行时获取标签:

let tag = Person::email::prost::tag;

1 个答案:

答案 0 :(得分:2)

由于属性仅在编译时是只读的,因此您需要编写过程宏来解决此问题。

您可以在宏中查找带有以下指示符的信息。

  • 字段名称为ident
  • 带有meta的属性内容

找到字段名称和元之后,可以将字符串化结果与宏中给定的输入参数进行匹配,如下所示:

macro_rules! my_macro {
    (struct $name:ident {
        $(#[$field_attribute:meta] $field_name:ident: $field_type:ty,)*
    }) => {
        struct $name {
            $(#[$field_attribute] $field_name: $field_type,)*
        }

        impl $name {
            fn get_field_attribute(field_name_prm : &str) -> &'static str {
                let fields = vec![$(stringify!($field_name,$field_attribute)),*];
                let mut ret_val = "Field Not Found";

                fields.iter().for_each(|field_str| {
                    let parts : Vec<&str> = field_str.split(' ').collect();
                    if parts[0] == field_name_prm{
                        ret_val = parts[2];
                    }
                });
                ret_val
            }
        }
    }
}

my_macro! {
    struct S {
        #[serde(default)]
        field1: String,
        #[serde(default)]
        field2: String,
    }
}

请注意,它假定结构中的每个字段都有一个属性。每个字段声明都以,结尾,包括最后一个字段。但是通过对正则表达式进行一些修改,您也可以将其用于可选属性。

Playground中的工作解决方案

有关指示符的更多信息,请访问reference

您还可以快速查看程序宏here