| Crates.io | dyn-inventory |
| lib.rs | dyn-inventory |
| version | 0.2.0 |
| created_at | 2025-09-23 20:25:11.323667+00 |
| updated_at | 2025-09-24 01:25:41.265329+00 |
| description | proc macro for building runtime plugin registries using dyn-compatible traits and the inventory crate. |
| homepage | |
| repository | https://github.com/joshua-auchincloss/dyn-inventory.git |
| max_upload_size | |
| id | 1852064 |
| size | 39,767 |
proc macro for building runtime plugin registries using dyn-compatible traits and the inventory crate.
this crate generates code to:
dyn trait)use dyn_inventory::dyn_inventory;
pub trait MyPlugin {
fn handle(&self);
}
dyn_inventory! {
Plugin<Handle: MyPlugin> {
pub name: &'static str,
desc: &'static str,
handle: Handle
}
}
mod my_plugin {
use crate::{MyPlugin, Plugin, PluginInit};
dyn_inventory::emit! {
Handle MyPlugin as Plugin {
name = "my plugin for abc-framework",
desc = "implements my plugin by doing xyz"
}
}
impl MyPlugin for Handle {
fn handle(&self) {
println!("MyPlugin was used");
}
}
}
fn main() {
let collected = PluginCollector::new();
for plugin in &collected.plugins {
plugin.handle.handle();
// >> "MyPlugin was used"
}
}
the plugins produced by this crate are stored and used as Box<dyn Trait>. when used with inventory, this allows for new plugin registries to be developed for decentralized libraries and frameworks.
[dependencies]
inventory = "0.3"
dyn-inventory = "0.2"
pub trait Greeter {
fn greet(&self) -> String;
}
dyn_inventory! proc macro:pub trait Greeter {
fn greet(&self) -> String;
}
dyn_inventory::dyn_inventory!(
GreeterPlugin<T: Greeter> {
name: &'static str,
version: u32,
t: T,
};
);
[!TIP] what this generates:
- a struct
GreeterPluginwith the fields you declared, and aBox<dyn Greeter>- an inventory registration type
inventory::collect!(GreeterPluginInit)- a collector
GreeterPluginCollectorthat haspluginof typeVec<GreeterPlugin>
use crate::{Greeter, GreeterPlugin, GreeterPluginInit};
use dyn_inventory::emit;
// this expands to a unit struct named `MyGreeter` and registers it into the inventory
emit! {
MyGreeter Greeter for GreeterPlugin {
name = "hello",
version = 1,
}
}
// you implement the trait for the generated unit struct
impl Greeter for MyGreeter {
fn greet(&self) -> String { "hi".to_string() }
}
let collected = GreeterPluginCollector::new();
for plugin in &collected.plugins {
// `plugin.t` is now a `Box<dyn Greeter>`; other fields are your metadata
println!("{} -> {}", plugin.name, plugin.t.greet());
}
use dyn_inventory::dyn_inventory;
dyn_inventory!(
// StructName = the name of the struct that holds the Box<dyn TraitName>
// TraitName - the trait which needs a dyn-inventory
StructName<Handle: TraitName> {
// exactly one field must have type `Handle`.
// the field whose type equals the generic parameter (`Generic`) is treated as the plugin “handle”.
// internally during registration this field is filled with a function pointer `fn() -> Box<dyn TraitName>`, and the collector converts it to `Box<dyn TraitName>` by calling it.
handle: Handle,
// optional visibity specifier
// any number of metadata fields are preserved
pub|pub(crate) field_name: &'static str,
pub other_field: usize,
};
// optional, comma-separated extra params
init_name = InitStructName,
);
two extra params are currently accepted:
init_name = ident
StructName (for example, GreeterPlugin -> greeter_plugin).the collector type is named by appending Collector to your struct name. it exposes:
new() -> builds the collection without modificationnew_with(|item: &mut StructName| {...}) -> allows you to mutate the raw entries after they are instantiated into Box<dyn TraitName>inventory crate must be linked into the final binary; ensure your plugin crates depend on inventory and your main binary pulls in the crates that perform registrations