this post was submitted on 10 Sep 2024
17 points (94.7% liked)

Rust

5938 readers
1 users here now

Welcome to the Rust community! This is a place to discuss about the Rust programming language.

Wormhole

[email protected]

Credits

  • The icon is a modified version of the official rust logo (changing the colors to a gradient and black background)

founded 1 year ago
MODERATORS
 

Hi all.

I want to develop a plugin system within my program, and I have a trait that functions defined by plugins should implement.

Currently, my code gets all the functions in a HashMap and then calls them by their name. Problem is, I have to create that hashmap myself by inserting every function myself.

I would really appreciate it if there was a way to say, suppose, all pub members of mod functions:: that implement this trait PluginFunction call register(hashmap) function. So as I add more functions as mod in functions it'll be automatically added on compile.

Pseudocode:

Files:

src/
├── attrs.rs
├── functions
│   ├── attrs.rs
│   ├── export.rs
│   └── render.rs
├── functions.rs
├── lib.rs

Basically, in mod functions I want:

impl AllFunctions{
    pub fn new() -> Self {
       let mut functions_map = HashMap::new();[[
       register_all!(crate::functions::* implementing PluginFunction, &mut functions_map);
       Self { function_map }
  }
}

Right now I'm doing:

impl AllFunctions{
    pub fn new() -> Self {
       let mut functions_map = HashMap::new();[[
       crate::functions::attrs::PrintAttr{}.register(&mut functions_map);
       crate::functions::export::ExportCSV{}.register(&mut functions_map);
       crate::functions::render::RenderText{}.register(&mut functions_map);
       // More as I add more functions
       Self { function_map }
  }
}
you are viewing a single comment's thread
view the rest of the comments
[–] [email protected] 1 points 1 month ago (1 children)

You could wrap the entirety of your file in a monster macro but you'd still have to assign the macro result to a variable you need to register, which doesn't sound viable to me at least.

Maybe you can use a script that would extract all the trait implementations and create the boilerplate glue code for you, something like this:

grep --recursive --only-matching "impl PluginFunction for \w*" functions/ | sed --quiet "s/functions\/\(.*\)\.rs:impl PluginFunction for \(\w*\)/crate::functions::\1::\2{}.register(\&mut functions_map)/p"

I tried to recreate your situation locally but it may not match perfectly, maybe you'll have to adjust it a little. When I run it on my file tree which looks like this

functions
├── attr.rs
├── export.rs
└── render.rs

1 directory, 3 files

where every file has a content like this

// comment

pub struct MyAttrStructName {}

impl PluginFunction for MyAttrStructName {

}

Then I receive the following output:

crate::functions::attr::MyAttrStructName{}.register(&mut functions_map)
crate::functions::export::MyExportStructName{}.register(&mut functions_map)
crate::functions::render::MyRenderStructName{}.register(&mut functions_map)
[–] [email protected] 1 points 1 month ago

Thank you. Yeah, something like this would work for me as I can add in a script and run it before compiling. But it won't be a cross platform solution and windows/mac users are probably not going to be able to do anything. Maybe if I do the same thing but from build.rs it'll work. I'll try that.