How can I get all structs that derive a macro?

Sorry if this already was asked.
I've just embarked on big rust exploration journey and my head hurts :sweat_smile:.
I found am ORM library GitHub - weldsorm/welds, where structs derive WeldsModel.

#[derive(WeldsModel)]
pub struct Something{};

And I'm looking for ways to generate a schema from them. I thought I can somehow get all structures that derive WeldsModel, build dependency graph and generate an sql querry from that, but it looks like, I'm thinking too JVM'ish.

Take this with a pile of salt as I'm new to Rust myself:

I don't know if its possible to get all structs that derive a macro on those terms, but a macro is a way to generate valid rust code, so one approach would be to take a look at the code this macro produces.

For example, from the macro source I can see it creates an implementation of HasSchema for the struct:

So maybe you could use trait bounds to interact with them:

trait HasSchema {
    fn foo(&self);
}

fn do_something<S>(some_struct: S)
where
    S: HasSchema,
{
    some_struct.foo()
}

Thank you for your suggestion! I'm currently exploring how can I do it externally using syn for parsing. Ideally, I want to generate migrations just like Hibernate or EF Core.
I assume it is possible to get to the structs through AST using syn::visit - Rust (docs.rs) and Meta in syn - Rust (docs.rs).

1 Like

If you can modify the derive macro, you can try to use Inventory.

1 Like

So, my current solution is this, now I'm somehow need to build a schema out of this :thinking: :

use std::fs::File;
use std::io::Read;
use syn::punctuated::Punctuated;
use syn::{ItemStruct, Meta, Token};

fn find_weldsmodel_structs(syntax_tree: syn::File) -> Vec<ItemStruct> {
    let mut weldsmodel_structs = Vec::new();

    for item in syntax_tree.items {
        if let syn::Item::Struct(item_struct) = item {
            if has_weldsmodel_derive(&item_struct) {
                weldsmodel_structs.push(item_struct);
            }
        }
    }

    weldsmodel_structs
}

fn has_weldsmodel_derive(item_struct: &ItemStruct) -> bool {
    let attrs = &item_struct.attrs;
    for attr in attrs {
        let meta = &attr.meta;
        if meta.path().is_ident("derive") {
            let nested = attr
                .parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
                .unwrap();
            for nested_meta in nested {
                if nested_meta.path().is_ident("WeldsModel") {
                    return true;
                }
            }
        }
    }
    false
}

fn main() {
    for entry in walkdir::WalkDir::new("welds/examples")
        .into_iter()
        .filter_map(Result::ok)
    {
        let path = entry.path();
        if path.is_file() && path.extension().unwrap() == "rs" {
            let mut file = File::open(path).expect("Unable to open file");
            let mut contents = String::new();
            file.read_to_string(&mut contents)
                .expect("Unable to read file");
            let syntax_tree = syn::parse_file(&contents).unwrap();
            let weldsmodel_structs = find_weldsmodel_structs(syntax_tree);
            for weldsmodel_struct in weldsmodel_structs {
                println!("Found struct: {}", weldsmodel_struct.ident);
            }
        }
    }
}
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.