在synv1中,有NestedMeta一个对于解析嵌套元非常方便的功能。但自synv2 以来,它已被删除。
例如,
trait Hello {
fn hello();
}
#[derive(Hello)]
#[hello("world1", "world2")]
struct A;
fn main() {
A::hello();
}
Run Code Online (Sandbox Code Playgroud)
我希望上面的代码打印Hello world1, world2!在屏幕上。我的 proc-macro 可以使用 v1 来实现,syn如下所示
use proc_macro::TokenStream;
use quote::quote;
use syn::{DeriveInput, Meta};
#[proc_macro_derive(Hello, attributes(hello))]
pub fn hello_derive(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let mut target: Vec<String> = vec![];
for attr in ast.attrs {
if let Some(attr_meta_name) = attr.path.get_ident() {
if attr_meta_name == "hello" {
let attr_meta = attr.parse_meta().unwrap();
match attr_meta {
Meta::List(list) => {
for p in list.nested {
match p {
NestedMeta::Lit(lit) => match lit {
Lit::Str(lit) => {
target.push(lit.value());
},
_ => {
panic!("Incorrect format for using the `hello` attribute.")
},
},
NestedMeta::Meta(_) => {
panic!("Incorrect format for using the `hello` attribute.")
},
}
}
},
_ => panic!("Incorrect format for using the `hello` attribute."),
}
}
}
}
if target.is_empty() {
panic!("The `hello` attribute must be used to set at least one target.");
}
let target = target.join(", ");
let expanded = quote! {
impl Hello for #name {
fn hello() {
println!("Hello {}.", #target);
}
}
};
expanded.into()
}
Run Code Online (Sandbox Code Playgroud)
但是当我尝试用synv2 重新实现它时,我坚持使用parse_nested_meta似乎拒绝文字的方法。
use proc_macro::TokenStream;
use quote::quote;
use syn::{DeriveInput, Meta};
#[proc_macro_derive(Hello, attributes(hello))]
pub fn hello_derive(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let mut target: Vec<String> = vec![];
for attr in ast.attrs {
if let Some(attr_meta_name) = attr.path().get_ident() {
if attr_meta_name == "hello" {
let attr_meta = attr.meta;
match attr_meta {
Meta::List(list) => {
list.parse_nested_meta(|meta| {
// I don't know how to handle this
Ok(())
})
.unwrap();
},
_ => panic!("Incorrect format for using the `hello` attribute."),
}
}
}
}
if target.is_empty() {
panic!("The `hello` attribute must be used to set at least one target.");
}
let target = target.join(", ");
let expanded = quote! {
impl Hello for #name {
fn hello() {
println!("Hello {}.", #target);
}
}
};
expanded.into()
}
Run Code Online (Sandbox Code Playgroud)
如何使用synv2解析属性?
似乎没有其他实现该特征的内置结构Parse可以做到这一点。所以我们必须自己创建一个。
use proc_macro::TokenStream;
use quote::quote;
use syn::{
parse::{Parse, ParseStream},
DeriveInput, LitStr, Meta, Token,
};
struct MyParser {
v: Vec<String>,
}
impl Parse for MyParser {
#[inline]
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
let mut v: Vec<String> = vec![];
loop {
if input.is_empty() {
break;
}
v.push(input.parse::<LitStr>()?.value());
if input.is_empty() {
break;
}
input.parse::<Token!(,)>()?;
}
Ok(MyParser {
v,
})
}
}
#[proc_macro_derive(Hello, attributes(hello))]
pub fn hello_world_derive(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let mut target: Vec<String> = vec![];
for attr in ast.attrs {
if attr.path().is_ident("hello") {
match attr.meta {
Meta::List(list) => {
let parsed: MyParser = list.parse_args().unwrap();
target.extend_from_slice(&parsed.v);
},
_ => panic!("Incorrect format for using the `hello` attribute."),
}
}
}
if target.is_empty() {
panic!("The `hello` attribute must be used to set at least one target.");
}
let target = target.join(", ");
let expanded = quote! {
impl Hello for #name {
fn hello() {
println!("Hello {}.", #target);
}
}
};
expanded.into()
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2019 次 |
| 最近记录: |