Rust - 过程宏

一、添加过程宏依赖库

1、过程宏,类似其他语言的运行时反射机制
2、官方的过程宏库为proc_macro,不推荐直接使用
3、推荐更友好的synquoteproc_macro2这3个库
4、过程宏,必须写在单独的lib类型的crate中

[lib]
proc-macro = true

[dependencies]
syn = "1.0.17"
quote = "1.0.3"
proc-macro2 = "1.0.10"

导入库

extern crate proc_macro;

use syn::{DeriveInput, parse_macro_input};
use quote::*;
use proc_macro::TokenStream;
二、使用syn解析derive过程宏的词条流,生成DeriveInput结构体

1、DeriveInput结构体,字段如下

    pub struct DeriveInput {
        pub attrs: Vec, 			// 属性,类似#[sqlx(A)]
        pub vis: Visibility, 				// 可见性
        pub ident: Ident, 					// 名称
        pub generics: Generics, 			// 泛型
        pub data: Data, 					// 类型数据,结构体、枚举体、联合体
    }

示例结构体

struct MyStruct 
    where T: Sized {
    a: String,
    b: Vec,
    c: T,
}

2、使用宏parse_macro_input!解析输入的词条流TokenStream,并打印结构体名称

#[proc_macro_derive(MyTest)]
pub fn derive_my_test(input: TokenStream) -> TokenStream {
    let derive_input = parse_macro_input!(input as DeriveInput);
    println!("struct name is : {}", derive_input.ident.to_token_stream());

	// ...
}

3、解析泛型相关的几个词条,并打印

    let (impl_generics, ty_generics, where_clause) = derive_input.generics.split_for_impl();

    println!("impl_generics: {}", impl_generics.to_token_stream()); // < T >
    println!("ty_generics: {}", ty_generics.to_token_stream()); // < T >
    println!("where_clause: {}", where_clause.to_token_stream()); // where T : Sized

4、解析结构体字段,可能是有名字段,也可能是无名字段,并打印

    match derive_input.data {
        syn::Data::Struct(ref data_struct) => match data_struct.fields {
            // field: (0) a: String
            // field: (1) b: Vec < u8 >
            // field: (2) c: T
            syn::Fields::Named(ref fields_named) => {
                for (index, field) in fields_named.named.iter().enumerate() {
                    println!("named struct field: ({}) {}: {}", index, field.ident.to_token_stream(), field.ty.to_token_stream())
                }
            },
            // field: (0) : String
            // field: (1) : Vec < u8 >
            // field: (2) : T
            syn::Fields::Unnamed(ref fields_unnamed) => {
                for (index, field) in fields_unnamed.unnamed.iter().enumerate() {
                    println!("unnamed struct field: ({}): {}", index, field.ty.to_token_stream())
                }
            },
            syn::Fields::Unit => {
                println!("unit struct field: None")
            },
        },
        _ => (),
    }

5、给derive添加属性(attributes),打印DeriveInput中的attrs

  • Attribute的结构体如下(重要的是pathtokens,这也是主要解析的部分):
    pub struct Attribute {
        pub pound_token: Token![#],
        pub style: AttrStyle,
        pub bracket_token: token::Bracket,
        pub path: Path,
        pub tokens: TokenStream,
    }
	// 实现方
	#[proc_macro_derive(ShowStruct, attributes(OptionDesc))]
	// 使用方
	#[derive(ShowStruct)]
	#[OptionDesc(ctx = "This is a description")]
	// 打印具体的解析信息
    let _attr: Vec<()> = derive_input.attrs.iter().map(|x| {
    	// 打印结果: path: OptionDesc, tokens: (ctx = "This is a description")
        println!("path: {}, tokens: {}", x.path.to_token_stream(), x.tokens);
    }).collect();

6、查看结构体的可见性,并打印

    match derive_input.vis {
        syn::Visibility::Public(ref vp) => {
            println!("pub struct : {}", vp.to_token_stream()); // pub
        },
        syn::Visibility::Crate(ref vc) => {
            println!("crate struct : {}", vc.to_token_stream()); // crate
        },
        syn::Visibility::Restricted(ref vr) => {
            println!("pub (crate | super | in some::module) struct : {}", vr.to_token_stream()); // pub (xxx)
        },
        _ => {
            println!("struct : {}", "inherited"); // inherited
        },
    }

7、创建新的ident,比如函数名称

    let struct_name = derive_input.ident;
    let fn_name = syn::Ident::new("show_struct", proc_macro2::Span::call_site());
三、使用quote!宏,生成proc_macro2的词条流

1、示例代码

    let proc_macro2_token_stream = quote! (
        impl #impl_generics #struct_name #ty_generics #where_clause {
            fn #fn_name(&self) {
                println!("Todo: ...")
            }
        }
    );

    TokenStream::from(proc_macro2_token_stream)
    // proc_macro2_token_stream.into()

2、使用#xxx捕获可以to_token_stream()的词条

你可能感兴趣的:(Rust)