Tricked.dev

Rust Plugin system

Today i created a dynamic plugin system in rust. It is a very simple system that allows you to load plugins at compile time. Wacky Plugins

How it works

it uses a build.rs to discover the plugins at compile time and add them to the Cargo.toml file. It then uses a macro to load the plugins at runtime.

fn main() -> Result<(), Box<dyn std::error::Error>> {
let manifest = fs::read_to_string("./Cargo.toml")?;
let mut doc = manifest.parse::<toml_edit::Document>()?;
let entries = fs::read_dir("../../plugins")?
.map(|res| res.map(|e| e.path()))
.collect::<Result<Vec<_>, io::Error>>()?;
for plugin in entries.clone() {
doc["dependencies"][plugin.file_name().unwrap().to_str().unwrap()]["path"] =
toml_edit::value(format!("{}", plugin.display()));
}
fs::write("./Cargo.toml", doc.to_string())?;
let entries_str = entries
.into_iter()
.map(|e| {
format_ident!(
"{}",
e.file_name()
.unwrap()
.to_string_lossy()
.to_string()
.replace("-", "_")
)
})
.collect::<Vec<_>>();
let result = quote! {
use plugin_lib::PluginTrait;
pub fn load_plugins() -> Vec<Box<dyn PluginTrait>> {
vec![
#(
Box::new(#entries_str::Plugin::new()),
)*
]
}
};
write(
format!("{}{}", env::var("OUT_DIR").unwrap(), "/plugin_shim.rs"),
result.to_string(),
)?;
Ok(())
}

And then you can use the plugins like this:

use plugin_loader::PLUGIN_REGISTRY;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
PLUGIN_REGISTRY.initialize().await?;
for plugin in PLUGIN_REGISTRY.plugins.lock().iter() {
println!(
"Plugin: {} initialized: {}",
plugin.name(),
plugin.initialized()
);
}
let input = "This is cool text!";
for plugin in PLUGIN_REGISTRY.plugins.lock().iter() {
println!(
"Plugin: {} processed text: {}",
plugin.name(),
plugin.text_process(input)
);
}
Ok(())
}

Why?

Having compile time checked plugins is great but having to recompile every time you want to add a plugin is not, but it may be worth it since you can use the full rust feature set async, all libraries, etc.

Limitations/Advantages

Advantages

Limitations

View on Github