将属性转换为proc宏派生时的标识符

问题描述

我正在编写我的第一个proc宏,尽管试图通读该错误,structopt和derive_more的源代码,但我似乎找不到确切的查找内容。我想改变这个:

#[derive(Attach)]
#[attach(foo(SomeType,OtherType))]
#[attach(bar(OtherType))]
struct Plugin {}

对此:

impl Attach for Plugin {
    fn attach(self,srv: &mut Server) {
        let this = Arc::new(self);
        srv.foo_order(this.clone(),&[TypeId::of::<SomeType>(),TypeId::of::<OtherType>()]);
        srv.bar_order(this,&[TypeId::of::<OtherType>()]);
    }
}

我已经开始编写proc宏,但在尝试解析属性时却陷入困境:

extern crate proc_macro;

use proc_macro::{Span,TokenStream};
use quote::quote;
use std::any::TypeId;
use syn::{
    parse::ParseStream,parse_macro_input,Attribute,AttributeArgs,DeriveInput,Ident,Result,};

#[proc_macro_derive(Attach,attributes(attach))]
pub fn register_macro(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    impl_register(input)
}

fn impl_register(input: DeriveInput) -> TokenStream {
    let name = &input.ident;
    let attrs = &input.attrs;

    for attr in attrs {
        if attr.path.is_ident("attach") {
            parse_attach_attribute(&attr);
        }
    }
    println!("{:#?}",input);

    TokenStream::from(quote! {
        impl ::corten::Attach for #name {
            fn attach(self,srv: &mut ::corten::Server) {

            }
        }
    })
}

fn parse_attach_attribute(attr: &Attribute) -> Result<Dependency> {
    let list: syn::MetaList = attr.parse_args()?;
    // println!("{:#?}",list);
    let ident = list.path.get_ident().expect("expected identifier");
    let method = Ident::new(&format!("{}_order",ident),ident.span());
    println!("{:#?}",method);
    let dependencies = list
        .nested
        .into_pairs()
        .map(|pair| pair.into_value())
        .collect::<Vec<_>>();
    // How does one get the identifiers out of a nestedMeta?
    println!("{:#?}",dependencies);
    // attr.parse_args_with(|input: ParseStream| {
    //     let ident = input.p
    //     let method = Ident::new(&format!("{}_order",Span::call_site());
    //     let dep = Dependency {
    //         method,//     }
    // })
    unimplemented!()
}
struct Dependency {
    method: Ident,dependencies: Vec<Ident>,}

我遇到的困难是如何将属性列表实际转换为可用形式?据我所知,我需要从&[Attribute]中解析“ foo”和“ bar”,以便构造方法标识符,以及“ SomeType”和“ OtherType”标识符,所有这些最终都馈入quote!中。如果我在控制台中打印TokenStream,则所有信息都在那里:

[
    Attribute {
        pound_token: Pound,style: Outer,bracket_token: Bracket,path: Path {
            leading_colon: None,segments: [
                PathSegment {
                    ident: Ident {
                        ident: "attach",span: #0 bytes(103..109),},arguments: None,],tokens: TokenStream [
            Group {
                delimiter: Parenthesis,stream: TokenStream [
                    Ident {
                        ident: "predecode",span: #0 bytes(110..119),Group {
                        delimiter: Parenthesis,stream: TokenStream [
                            Ident {
                                ident: "SomeType",span: #0 bytes(120..128),Punct {
                                ch: ',',spacing: Alone,span: #0 bytes(128..129),Ident {
                                ident: "OtherType",span: #0 bytes(130..139),span: #0 bytes(119..140),span: #0 bytes(109..141),Attribute {
        pound_token: Pound,span: #0 bytes(145..151),stream: TokenStream [
                    Ident {
                        ident: "preresolve",span: #0 bytes(152..162),stream: TokenStream [
                            Ident {
                                ident: "OtherType",span: #0 bytes(163..172),span: #0 bytes(162..173),span: #0 bytes(151..174),]

但是我没有办法真正做到这一点。我怎么去tokens[0].stream.ident

解决方法

经过一整天的思考后, 我有一些可行的方法,尽管我很乐意接受其他更好的答案,因为我觉得这有点混乱:

extern crate proc_macro;

use proc_macro2::{Span,TokenStream};
use quote::quote;
use syn::{parse_macro_input,Attribute,DeriveInput,Ident,Result};

#[proc_macro_derive(Attach,attributes(attach))]
pub fn register_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    proc_macro::TokenStream::from(impl_register(input))
}

fn impl_register(input: DeriveInput) -> TokenStream {
    let name = &input.ident;
    let attrs = &input.attrs;
    // println!("{:#?}",input);

    let attrs = attrs
        .iter()
        .filter(|attr| attr.path.is_ident("attach"))
        .map(|attr| parse_attach_attribute(&attr).expect("parse failed"))
        .map(|dep| {
            let method: Ident = dep.method;
            let dependencies = dep.dependencies.iter().map(|ident: &Ident| {
                quote! {
                    std::any::TypeId::of::<#ident>()
                }
            });
            quote! {
                srv.#method::<#name,_>(Arc::clone(&this),&[ #(#dependencies),* ]);
            }
        });

    quote! {
        impl corten::Attach for #name {
            fn attach(self,srv: &mut corten::Server) {
                let this = std::sync::Arc::new(self);
                #(#attrs)*
            }
        }
    }
}

fn parse_attach_attribute(attr: &Attribute) -> Result<Dependency> {
    let list: syn::MetaList = attr.parse_args()?;
    // println!("{:#?}",list);
    let ident = list.path.get_ident().expect("expected identifier");
    let method = Ident::new(&format!("{}_order",ident),Span::call_site());
    println!("{:#?}",method);
    let dependencies = list
        .nested
        .into_pairs()
        .map(|pair| pair.into_value())
        .filter_map(|pair| match pair {
            syn::NestedMeta::Meta(meta) => match meta {
                syn::Meta::Path(path) => path.get_ident().cloned(),_ => panic!("only path meta supported"),},_ => panic!("lit not supported"),})
        .collect();
    println!("{:#?}",dependencies);

    Ok(Dependency {
        method,dependencies,})
}
struct Dependency {
    method: Ident,dependencies: Vec<Ident>,}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...