如何使用darling解析支持多形式的带嵌套参数的自定义派生属性?
解决Darling解析多形式自定义属性的问题
你的问题出在**MyAttr结构体默认的FromMeta实现不支持单纯的标识符(也就是att这种不带括号的形式)**。Darling对结构体类型的默认解析逻辑只接受带括号的键值对(比如att(...)),而当你写#[myderive(att)]时,它会把att识别为一个Meta::Path类型的元数据,和结构体期望的格式不匹配,所以抛出了UnexpectedFormat("word")错误。
要实现你想要的三种使用形式,我们需要为MyAttr自定义FromMeta实现,让它同时支持两种输入格式:单纯的标识符(无参数)和带括号的参数列表(带嵌套参数)。
正确的代码实现
首先修改你的类型定义,为MyAttr添加自定义的FromMeta逻辑:
use darling::{FromDeriveInput, FromMeta, Error}; use syn::{Meta, Path}; #[derive(FromDeriveInput)] #[darling(attributes(myderive))] struct MyDeriveInput { #[darling(default)] att: Option<MyAttr>, } #[derive(Debug, Clone, PartialEq, FromMeta, Default)] struct MyAttr { #[darling(default)] value: Option<Path>, } // 自定义FromMeta实现,支持单纯的标识符和带括号的参数 impl FromMeta for MyAttr { fn from_meta(meta: &Meta) -> Result<Self, Error> { // 处理 `att` 这种无参数的情况:直接返回默认的MyAttr(value为None) if let Meta::Path(_) = meta { Ok(Self::default()) } else { // 处理 `att(value = "String")` 这种带参数的情况:调用派生的FromMeta实现解析括号内的内容 <Self as FromMeta>::from_meta(meta) } } }
验证测试代码
你的测试现在可以正常通过了:
#[test] fn test_att_without_args() { let derive_input = syn::parse_str( r#" #[derive(MyTrait)] #[myderive(att)] struct Foo(u64); "#, ) .unwrap(); let parsed: MyDeriveInput = FromDeriveInput::from_derive_input(&derive_input).unwrap(); assert!(parsed.att.is_some()); assert!(parsed.att.unwrap().value.is_none()); }
验证其他两种场景
你也可以添加测试来验证另外两种场景:
#[test] fn test_no_att() { let derive_input = syn::parse_str( r#" #[derive(MyTrait)] struct Foo(u64); "#, ) .unwrap(); let parsed: MyDeriveInput = FromDeriveInput::from_derive_input(&derive_input).unwrap(); assert!(parsed.att.is_none()); } #[test] fn test_att_with_value() { let derive_input = syn::parse_str( r#" #[derive(MyTrait)] #[myderive(att(value = "String"))] struct Foo(u64); "#, ) .unwrap(); let parsed: MyDeriveInput = FromDeriveInput::from_derive_input(&derive_input).unwrap(); assert!(parsed.att.is_some()); assert!(parsed.att.unwrap().value.is_some()); assert_eq!(parsed.att.unwrap().value.unwrap().segments[0].ident.to_string(), "String"); }
原理说明
- 当解析
#[myderive(att)]时,meta是Meta::Path类型,我们直接返回MyAttr的默认值(value: None)。 - 当解析
#[myderive(att(value = "String"))]时,meta是Meta::List类型,我们调用派生的FromMeta实现来解析括号内的value参数。 - 当不写
att时,MyDeriveInput的att字段因为标记了#[darling(default)],会自动设为None。
这样就完美支持了你需要的三种使用形式。
内容的提问来源于stack exchange,提问作者Peter Hall
相关产品推荐
相关产品推荐

