1

I am using struct to build the schema of the database, so I need to meta information of struct.

Suppose my schema struct is defined like below:

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

I want to parse into the following struct:

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
}

I wrote a beginning, but I don’t know how to continue writing:

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

Expected result:

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

I don't know how to implement it.

I have only recently begun to learn derive macro and have not fully mastered it yet. It is difficult to find useful tutorials about derive macro on the Internet.

Please help me, thanks.

pandalive
  • 75
  • 1
  • 1
  • 6
  • 1
    Attribute is a thing inside `#[..]`, you probably meant *field* – Kitsu Jul 10 '20 at 15:53
  • Does this answer your question? [Is there is a way to get the field names of a struct in a macro?](https://stackoverflow.com/questions/29986057/is-there-is-a-way-to-get-the-field-names-of-a-struct-in-a-macro) – elliptic_hyperboloid Jul 10 '20 at 16:07
  • As far as I know this is not possible. However as mentioned in [this answer](https://stackoverflow.com/questions/29986057/is-there-is-a-way-to-get-the-field-names-of-a-struct-in-a-macro#29986760) you can serialize the object to JSON and then read the field names back. But you should ask yourself if this is really what you need. – elliptic_hyperboloid Jul 10 '20 at 16:08

1 Answers1

5

Use proc_macro_derive(procedural macro)

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();
}

Output:

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's source code on github

Reference:

pylint
  • 131
  • 1
  • 4