首页 > 解决方案 > rust - 如何使用派生宏来解析结构的元信息?

问题描述

我正在使用struct来构建数据库的架构,所以我需要struct的元信息。

假设我的模式结构定义如下:

#[derive(ParseStruct)]
pub struct User {

  #[field_attr( unique = true, default = "", index=["@hash", "@count"] )]
  pub name: String,

  #[field_attr( unique = true, default = "" )]
  pub username: String,

  pub description: Option<String>,
  pub age: u32
}

我想解析成以下结构:

pub struct Schema<T>{
  name: String,         // the struct name
  origin: T,            // the struct to be parse, like the User struct above.
  fields: Vec<Field>,   // the struct's fields
}

pub struct Field {
    field_name: String,
    field_type: String,
    field_attrs: FieldAttribute
}

pub struct FieldAttribute {
    unique: bool,
    default: String,
    index: Vec<String>   // Index of the database
}

我写了一个开头,但我不知道如何继续写:

#[proc_macro_derive(ParseStruct)]
pub fn parse_struct_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    let name = &ast.ident;
    
    let gen = quote! {
        impl #name {
            fn parsed_schema()-> Schema { 
               // return the parsed schema     
               //...           
            }
        }
    };
    gen.into()
}

预期结果:

use parse_struct::ParseStruct;

#[derive(ParseStruct)]
struct User{
    #[field_attr( unique = true, default = "", index=["@hash", "@count"] )]
    pub name: String
}

fn main() {
    let schema = User::parsed_schema();
    println!("{:#?}",schema);
}

我不知道如何实现它。

我最近才开始学习派生宏,还没有完全掌握。在 Internet 上很难找到有关派生宏的有用教程。

请帮助我,谢谢。

标签: structrustmacros

解决方案


使用 proc_macro_derive(过程宏)

src/main.rs:

use print_struct_trait::PrintStruct;

#[derive(PrintStruct)]
struct Point {
    name: String,
    x: i32,
    y: i32,
}

fn main() {
    let point = Point {
        name: "origin".to_string(),
        x: 2,
        y: 3,
    };
    point.print();
}

输出:

key=name, value=origin, type=String
key=x, value=2, type=i32
key=y, value=3, type=i32

print_struct_derive/src/lib.rs:

use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::spanned::Spanned;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(PrintStruct, attributes(serde))]
pub fn derive_signature(item: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(item as DeriveInput);
    let struct_name = &ast.ident;

    let fields = if let syn::Data::Struct(syn::DataStruct {
        fields: syn::Fields::Named(ref fields),
        ..
    }) = ast.data
    {
        fields
    } else {
        panic!("Only support Struct")
    };

    let mut keys = Vec::new();
    let mut idents = Vec::new();
    let mut types = Vec::new();

    for field in fields.named.iter() {
        let field_name: &syn::Ident = field.ident.as_ref().unwrap();
        let name: String = field_name.to_string();
        let literal_key_str = syn::LitStr::new(&name, field.span());
        let type_name = &field.ty;
        keys.push(quote! { #literal_key_str });
        idents.push(&field.ident);
        types.push(type_name.to_token_stream());
    }

    let expanded = quote! {
        impl PrintStruct for #struct_name {
            fn print(&self) {
                #(
                    println!(
                        "key={key}, value={value}, type={type_name}",
                        key = #keys,
                        value = self.#idents,
                        type_name = stringify!(#types)
                    );
                )*
            }
        }
    };
    expanded.into()
}

github上PrintStruct的源代码

参考:


推荐阅读