ржавчина - как использовать макрос derive для анализа метаинформации структуры? - PullRequest
0 голосов
/ 10 июля 2020

Я использую структуру для построения схемы базы данных, поэтому мне нужна метаинформация структуры.

Предположим, моя структура схемы определена, как показано ниже:

#[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);
}

Я не знаю, как это реализовать.

Я только недавно начал изучать макрос derive и еще не освоил его полностью. Трудно найти полезные руководства по макросу derive на Inte rnet.

Пожалуйста, помогите мне, спасибо.

1 Ответ

1 голос
/ 16 июля 2020

Использовать 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()
}

Исходный код PrintStruct на github

Ссылка:

...