Sorry if this already was asked.
I've just embarked on big rust exploration journey and my head hurts
.
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
:
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