如何用Rust的deluxe库提取带#[cacheable(id)]标记的字段名?
实现方案
首先你需要先定义字段对应的属性结构体,用来匹配#[cacheable(id)]标记,再遍历结构体字段提取目标字段名,最后生成Trait实现代码。
1. 定义字段属性结构体
先补充字段层面的属性解析结构体,关联到cacheable属性组:
use deluxe::{ExtractAttributes, ParseAttributes}; use quote::quote; use syn; // 结构体层面的属性(你已定义的部分) #[derive(Debug, ExtractAttributes, ParseAttributes)] #[deluxe(attributes(cacheable))] struct CacheableAttributes { directory: String, } // 字段层面的属性,匹配#[cacheable(id)]标记 #[derive(Debug, Default, ExtractAttributes, ParseAttributes)] #[deluxe(attributes(cacheable))] struct CacheableFieldAttributes { id: bool, // 标记当前字段是否为id字段 }
2. 提取并验证id字段
在派生宏实现中,遍历结构体字段,提取标记了id的字段,并做合法性校验:
fn cacheable_derive_impl( item: proc_macro2::TokenStream, ) -> deluxe::Result<proc_macro2::TokenStream> { // 解析输入的结构体定义 let mut ast: syn::DeriveInput = syn::parse2(item)?; // 提取结构体上的directory属性 let CacheableAttributes { directory } = deluxe::extract_attributes(&mut ast)?; // 提取并验证id字段 let id_field_name = match &mut ast.data { syn::Data::Struct(s) => { let mut id_fields = Vec::new(); for field in &mut s.fields { // 提取当前字段的cacheable属性 let field_attrs = CacheableFieldAttributes::extract_attributes(field)?; if field_attrs.id { // 仅处理有名字的结构体字段(排除元组结构体的无名字段) if let Some(ident) = &field.ident { id_fields.push(ident.clone()); } else { return Err(deluxe::Error::custom("id字段不能是元组结构体的无名字段").span(field.span())); } } } // 校验id字段数量:必须有且仅有一个 match id_fields.len() { 0 => return Err(deluxe::Error::custom("结构体必须包含一个标记了#[cacheable(id)]的字段").span(ast.span())), 1 => id_fields.remove(0), _ => return Err(deluxe::Error::custom("结构体只能有一个标记了#[cacheable(id)]的字段").span(ast.span())), } } // 仅支持结构体类型 syn::Data::Enum(_) | syn::Data::Union(_) => { return Err(deluxe::Error::custom("Cacheable派生宏仅支持结构体类型").span(ast.span())); } }; // 生成Cacheable Trait的实现代码 let struct_name = &ast.ident; let expanded = quote! { impl Cacheable for #struct_name { fn id(&self) -> String { self.#id_field_name.to_string() } fn cache_directory() -> &'static str { #directory } } }; Ok(expanded) }
3. 配套定义Cacheable Trait
确保你已经定义了对应的Trait供派生宏实现:
pub trait Cacheable { /// 返回唯一标识字符串 fn id(&self) -> String; /// 返回缓存目录路径 fn cache_directory() -> &'static str; }
关键说明
- 字段属性结构体
CacheableFieldAttributes通过#[deluxe(attributes(cacheable))]与cacheable属性组关联,id: bool会自动匹配#[cacheable(id)]这种无值标记。 - 遍历字段时做了合法性校验:必须存在且仅存在一个id字段,且不能是元组结构体的无名字段,避免非法使用场景。
- 最终通过
quote库生成Trait实现代码,直接使用提取到的字段名id_field_name访问目标字段。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

