Automatically include modules from another directory and run a function at compile time

I want to include all files from another directory as modules, and run a specified function, without having to edit the code of the main file for new files being added.

I'm not good enough at macro wizardry to know where to start on this, and writing a build.rs script that edits the source code of the program right before compilation seems a bit janky/"un-rustic."

Here's an example of the desired output:

Directory tree:

.
├── main.rs
└── stuff
    ├── bar.rs
    └── foo.rs

foo.rs:

pub fn init() {
    println!("foo");
}

bar.rs:

pub fn init() {
    println!("bar");
}

main.rs:

fn main() {
    some_macro!("./stuff");
}

stdout:

foo
bar

I could use the include! macro for this, but as far as i know that would require another file that has nothing but a use statement and calling the function, which is also unwanted.

Solution using include!:

fooloader.rs:

use crate::foo;
foo::init();

main.rs:

fn main() {
    include!("./stuff/fooloader.rs");
    include!("./stuff/barloader.rs");
}

If i had instead included foo.rs, main.rs would expand to something like this:

fn main() {
    pub fn init() {
        println!("foo");
    }
}

(This also couldn't work because include! only takes in string literals, and it can't read directories)

Rust doesn't support that. Every solution to this will be janky. You should just write mod directives for these files ahead of time in the source code.

You shouldn't modify files in src at build time. The next best janky solution is to generate files using build.rs and place them in OUT_DIR, and then in the main file use env!("OUT_DIR") to find them.

1 Like

I'm trying to implement this, but i'm running into an issue: i can't split the OUT_DIR variable into other string literals, thus making it impossible to include! them. Would i need to make something like OUT_DIR1, OUT_DIR2, etc for this to work?

OUT_DIR is a path to a directory which your build script may write files with any name it likes into. You should have your build script generate one file which contains all the mods you want, and you can then include it like this:

include!(concat!(env!("OUT_DIR"), "/generated_mods.rs"));

No string splitting needed, only concatenation.

I used this, and it worked! Thank you all for your help! I also managed to include a .yaml file so users can define custom paths to each module and enable/disable them without removing the directory.