rich_plugin_api/linked.rs
1//! Compile-time plugins: registered with [`export_plugin!`](crate::export_plugin)
2//! and collected at link time.
3//!
4//! A plugin crate writes `rich_plugin_api::export_plugin!(MyPlugin);` once.
5//! Every such plugin in the final binary is listed by [`linked_plugins`], and
6//! a host (`rs-rich-ext`'s `ExtensionRegistry::with_linked_plugins`) adds them
7//! in a fixed order: sorted by plugin id, with a duplicate id an error.
8//!
9//! Collection uses [`inventory`], which needs no proc macro and works on every
10//! platform `rich` supports. One caveat, shared by every link-time scheme: a
11//! crate the binary depends on but never names may be dropped by the linker.
12//! Name it once (`use my_plugin as _;`) to keep its plugins.
13
14use crate::Plugin;
15
16/// One plugin submitted with [`export_plugin!`](crate::export_plugin).
17pub struct LinkedPlugin {
18 make: fn() -> Box<dyn Plugin>,
19 module: &'static str,
20}
21
22impl LinkedPlugin {
23 /// Used by [`export_plugin!`](crate::export_plugin); not called directly.
24 #[doc(hidden)]
25 pub const fn new(make: fn() -> Box<dyn Plugin>, module: &'static str) -> Self {
26 LinkedPlugin { make, module }
27 }
28
29 /// A fresh instance of the plugin.
30 pub fn plugin(&self) -> Box<dyn Plugin> {
31 (self.make)()
32 }
33
34 /// The module that exported it (`module_path!()`), for error messages.
35 pub fn module(&self) -> &'static str {
36 self.module
37 }
38}
39
40inventory::collect!(LinkedPlugin);
41
42/// Every plugin exported with [`export_plugin!`](crate::export_plugin) in this
43/// binary, in no particular order (a host sorts them by id).
44pub fn linked_plugins() -> impl Iterator<Item = &'static LinkedPlugin> {
45 inventory::iter::<LinkedPlugin>.into_iter()
46}
47
48#[doc(hidden)]
49pub mod __private {
50 pub use inventory;
51}
52
53/// Register a plugin for link-time collection.
54///
55/// The argument is an expression that makes the plugin, evaluated each time
56/// a host asks for it. It must not capture anything:
57///
58/// ```
59/// use rich_plugin_api::{Plugin, PluginError, PluginMetadata, PluginRegistrar};
60///
61/// struct Hello;
62/// impl Plugin for Hello {
63/// fn metadata(&self) -> PluginMetadata {
64/// PluginMetadata::new("hello", "Hello", "1.0.0")
65/// }
66/// fn register(&self, _: &mut dyn PluginRegistrar) -> Result<(), PluginError> {
67/// Ok(())
68/// }
69/// }
70///
71/// rich_plugin_api::export_plugin!(Hello);
72///
73/// # fn main() {
74/// assert!(rich_plugin_api::linked_plugins().any(|p| p.plugin().metadata().id == "hello"));
75/// # }
76/// ```
77#[macro_export]
78macro_rules! export_plugin {
79 ($plugin:expr) => {
80 $crate::__private::inventory::submit! {
81 $crate::LinkedPlugin::new(
82 || -> ::std::boxed::Box<dyn $crate::Plugin> { ::std::boxed::Box::new($plugin) },
83 ::core::module_path!(),
84 )
85 }
86 };
87}