rust 過程宏例子(rust編程之道的過程宏例子)

 Cargo.toml

[package]
name = "ioc_macro"
version = "0.1.0"
edition = "2018"

[dependencies]
syn = "1.0"
quote = "1.0"
proc-macro2 = "1.0.7"

[lib]
proc_macro = true

 

lib.rs

extern crate proc_macro;

use {
    syn::{Token, DeriveInput, parse_macro_input},
    quote::*,
    proc_macro2,
    proc_macro::TokenStream,
};

#[proc_macro_derive(new)]
pub fn derive(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    let result = match ast.data {
        syn::Data::Struct(ref s) => new_for_struct(&ast, &s.fields),
        _ => panic!("doesn't work with unions yet"),
    };
    result.into()
}

fn new_for_struct(ast: &syn::DeriveInput, fields: &syn::Fields) -> proc_macro2::TokenStream {
    match *fields {
        syn::Fields::Named(ref fields) => {
            new_impl(&ast, Some(&fields.named), true)
        }
        syn::Fields::Unit => {
            new_impl(&ast, None, false)
        }
        syn::Fields::Unnamed(ref fields) => {
            new_impl(&ast, Some(&fields.unnamed), false)
        }
    }
}

fn new_impl(ast: &syn::DeriveInput, fields: Option<&syn::punctuated::Punctuated<syn::Field, Token![,]>>, named: bool) -> proc_macro2::TokenStream
{
    let struct_name = &ast.ident;
    let unit = fields.is_none();
    let empty = Default::default();
    let fields: Vec<_> = fields.unwrap_or(&empty).iter().
        enumerate()
        .map(|(i, f)| FieldExt::new(f, i, named)).collect();
    let args = fields.iter().map(|f| f.as_arg());
    let inits = fields.iter().map(|f| f.as_init());
    let inits = if unit {
        quote!()
    } else if named {
        quote![{ #(#inits), *}]
    } else {
        quote![( #(#inits), *)]
    };
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
    let (new, doc) = (
        syn::Ident::new("new", proc_macro2::Span::call_site()),
        format!("Constructs a new `{}`.", struct_name)
    );
    quote! {
         impl #impl_generics #struct_name #ty_generics #where_clause {
                 #[doc=#doc]
                 pub fn #new(#(#args),*) ->Self {
                    #struct_name #inits
                 }
           }
        }
}

struct FieldExt<'a> {
    ty: &'a syn::Type,
    //存儲類型
    ident: syn::Ident,
    // 結構體信息
    named: bool,//是否是有名字的結構體
}

impl<'a> FieldExt<'a> {
    pub fn new(field: &'a syn::Field, idx: usize, named: bool) -> FieldExt<'a> {
        FieldExt {
            ty: &field.ty,
            ident: if named {
                field.ident.clone().unwrap()
            } else {
                syn::Ident::new(
                    &format!("f{}", idx),
                    proc_macro2::Span::call_site())
            },
            named: named,
        }
    }
    pub fn as_arg(&self) -> proc_macro2::TokenStream {
        let f_name = &self.ident;
        let ty = &self.ty;
        quote!( #f_name: #ty)
    }
    pub fn as_init(&self) -> proc_macro2::TokenStream {
        let f_name = &self.ident;
        let init = quote!(#f_name);
        if self.named {
            quote!(#f_name: #init)
        } else {
            quote!( #init)
        }
    }
}

test.rs

use ioc_macro::new;

#[derive(new)]
struct Person {
    name: String,
}

#[test]
fn call() {
    let person: Person = Person::new("rust".into());
    println!("person name is {}", person.name);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章