关于rust宏补充(二)-派生过程宏示例

rust中过程宏示例:

准备工作

[lib]
proc-macro = true

[dependencies]
syn = { version = "1.0", features = ["full"] }
quote = "1.0"
proc-macro2 = "1.0"

定义一个过程宏: proc_macro_derive

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{self, parse_macro_input, Data, DataStruct, DeriveInput, Fields, FieldsNamed, Type};

#[proc_macro_derive(Builder, attributes(builder))]
pub fn derive(input: TokenStream) -> TokenStream {
    eprintln!("===input: {:#?}", input);
    // Parse the input tokens into a syntax tree.
    let input = parse_macro_input!(input as DeriveInput);
    // eprintln!("{:?}", format!("===ast: {}", input.into()));
    eprintln!("===ast input atrrs: {:#?}", input.attrs);
    eprintln!("===ast input name: {:#?}", input.ident);
    eprintln!("===ast input vis: {:#?}", input.vis);
    eprintln!("===ast input generics: {:#?}", input.generics);
    eprintln!("===ast input data: {:#?}", input.data);
    // Used in the quasi-quotation below as `#name`.
    let name = input.ident;

    // 类似:CommonBuilder格式
    let builder = format_ident!("{}Builder", name);

    /// 获取struct/enum/union的内容部分
    /// 类似struct的如下内容:DataStruct.data部分
    /// {
    ///     executable: String,
    ///     args: Vec,
    ///     env: Vec,
    ///     current_dir: String,
    /// }
    /// 经过使用syn库转换为syn.AST 大概格式如下:
    /// Struct(
    ///     DataStruct {
    ///         struct_token: Struct,           # 标识当前是一个struct
    ///         fields: Named(                  # 在当前struct中定义的field
    ///             FieldsNamed {               # 字段都是其命名的
    ///                 brace_token: Brace,
    ///                 named: [                # 命名field
    ///                     Field {             # 类似: executable: String,
    ///                         attrs: [],      # 该field在定义时指定了attributes
    ///                         vis: Inherited, # 该field的可见性
    ///                         ident: Some(    # 该field的名称及代码中位置
    ///                             Ident {
    ///                                 ident: "executable",  # 名称
    ///                                 span: #0 bytes(1016..1026), # 位置
    ///                             },
    ///                         ),
    ///                         colon_token: Some( # 冒号:
    ///                             Colon,
    ///                         ),
    ///                         ty: Path(           # 该field类型
    ///                             TypePath {      # 类型路径: 类似A::B::C
    ///                                 qself: None,
    ///                                 path: Path {
    ///                                     leading_colon: None,
    ///                                     segments: [
    ///                                         PathSegment {
    ///                                             ident: Ident {
    ///                                                 ident: "String",
    ///                                                 span: #0 bytes(1028..1034),
    ///                                             },
    ///                                             arguments: None,
    ///                                         },
    ///                                     ],
    ///                                 },
    ///                             },
    ///                         ),
    ///                     },
    ///                     Comma,
    ///                 ],
    ///             },
    ///         ),
    ///         semi_token: None,
    ///     },
    /// )

    // 获取当前struct中所有fields: DataStruct::fields::FieldsNamed部分
    let data: FieldsNamed = match input.data {
        Data::Struct(DataStruct {
            fields: Fields::Named(n),
            ..
        }) => n,
        other => unimplemented!("{:?}", other),
    };

    // 遍历每个命名的field:名称、类型; 构建(名称, 类型Option<真实类型>)
    let fields = data.named.iter().filter_map(|field| {
        let ty = &field.ty;
        match &field.ident {
            Some(ident) => Some((ident, ty, inner_for_option(ty))),
            _ => None,
        }
    });

    // 遍历每个field:名称、类型; 构建(名称, Option<真实类型>)
    let names = data.named.iter().filter_map(|field| match &field.ident {
        None => None,
        Some(ident) => Some((ident, inner_for_option(&field.ty))),
    });

    // 构建每个字段的初始化值:None; 类似 字段name: None
    let initialize = names.clone().map(|(name, _)| quote! { #name: None });

    //
    let extract = names.clone().map(|(name, option)| match option {
        None => quote! { #name: self.#name.clone()? },
        Some(_) => quote! { #name: self.#name.clone() },
    });

    // 构建所有字段的模版: 类似 字段name: 类型Option<#ty>
    let quoted_fields = fields.clone().map(|(name, ty, option)| match option {
        None => quote! { #name: Option<#ty> },
        Some(ty) => quote! { #name: Option<#ty> },
    });

    // 构建每个字段对应的setter方法
    // 格式类似:
    // pub fn 字段名称(&mut self, 值value: 类型#ty) {
    //  self.#name = Some(value);
    //  self
    // }
    let methods = fields.clone().map(|(name, ty, option)| match option {
        None => quote! {
            pub fn #name(&mut self, value: #ty) -> &mut Self {
                self.#name = Some(value);
                self
            }
        },

        Some(ty) => quote! {
            pub fn #name(&mut self, value: #ty) -> &mut Self {
                self.#name = Some(value);
                self
            }
        },
    });

    // 构建最终的Builder模式的对应的模版
    let expanded = quote! {
        // 生成Builder,并初始化struct不同字段的内容
        impl #name {
            fn builder() -> #builder {
                #builder {
                    #(
                        #initialize,
                    )*
                }
            }
        }

        // 定义Builder
        struct #builder {
            #(
                #quoted_fields,
            )*
        }

        // 实现Builder的build及不同字段赋值的方法
        impl #builder {
            pub fn build(&self) -> Option<#name> {
                Some(#name {
                    #(
                        #extract,
                    )*
                })
            }

            #(
                #methods
            )*
        }
    };
    eprintln!("===expanded: {:#?}", expanded);

    // 最终输出proc_macro::TokenStream,并入被编译器rustc输出AST
    TokenStream::from(expanded)
}

fn inner_for_option(ty: &Type) -> Option {
    match ty {
        Type::Path(syn::TypePath {
            path: syn::Path { segments, .. },
            ..
        }) if segments[0].ident == "Option" => {
            let segment = &segments[0];

            match &segment.arguments {
                syn::PathArguments::AngleBracketed(generic) => {
                    match generic.args.first().unwrap() {
                        syn::GenericArgument::Type(ty) => Some(ty.clone()),
                        _ => None,
                    }
                }
                _ => None,
            }
        }

        _ => None,
    }
}

测试

use derive_builder::Builder;

#[derive(Builder)]
pub struct Command {
    executable: std::string::String,
    args: Vec,
    env: Vec,
    current_dir: String,
}

fn main() {}
use derive_builder::Builder;

#[derive(Builder)]
pub struct Command {
    executable: String,
    args: Vec,
    env: Vec,
    current_dir: String,
}

fn main() {
    let builder = Command::builder();

    let _ = builder;
}

更多例子

补充

派生式过程宏解析

你可能感兴趣的:(关于rust宏补充(二)-派生过程宏示例)