
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
- You dont have to use extern c and ffi
- A plugin is a trait and compile time checked
- Its cross platform
Limitations
- You have to recompile every time you add a plugin
- You can dynamically load plugins
- Plugins have to be written in rust