openrr_plugin/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3#![allow(non_local_definitions)] // triggered for code generated by abi_stable macros
4
5mod proxy;
6
7#[rustfmt::skip]
8#[path = "gen/api.rs"]
9mod api;
10
11use std::{
12    fmt,
13    path::Path,
14    sync::{Arc, LazyLock},
15};
16
17use abi_stable::library::lib_header_from_path;
18
19pub use crate::api::*;
20// This is not a public API. Use export_plugin! macro for plugin exporting.
21#[doc(hidden)]
22pub use crate::proxy::PluginMod_Ref;
23
24/// Exports the plugin that will instantiated with the specified expression.
25///
26/// # Examples
27///
28/// ```
29/// use openrr_plugin::Plugin;
30///
31/// openrr_plugin::export_plugin!(MyPlugin);
32///
33/// pub struct MyPlugin;
34///
35/// impl Plugin for MyPlugin {
36/// }
37/// ```
38#[macro_export]
39macro_rules! export_plugin {
40    ($plugin_constructor:expr $(,)?) => {
41        /// Exports the root module of this library.
42        ///
43        /// This code isn't run until the layout of the type it returns is checked.
44        #[allow(clippy::drop_non_drop)] // this lint is triggered for code generated by #[export_root_module]
45        #[::abi_stable::export_root_module]
46        pub fn instantiate_root_module() -> $crate::PluginMod_Ref {
47            $crate::PluginMod_Ref::new(plugin_constructor)
48        }
49
50        /// Instantiates the plugin.
51        #[allow(clippy::drop_non_drop)] // this lint is triggered for code generated by #[export_root_module]
52        #[::abi_stable::sabi_extern_fn]
53        pub fn plugin_constructor() -> $crate::PluginProxy {
54            $crate::PluginProxy::new($plugin_constructor)
55        }
56    };
57}
58
59impl PluginProxy {
60    /// Loads a plugin from the specified path.
61    pub fn from_path(path: impl AsRef<Path>) -> Result<Self, arci::Error> {
62        let path = path.as_ref();
63
64        let header = lib_header_from_path(path).map_err(anyhow::Error::from)?;
65        let root_module = header
66            .init_root_module::<PluginMod_Ref>()
67            .map_err(anyhow::Error::from)?;
68
69        let plugin_constructor = root_module.plugin_constructor();
70        let plugin = plugin_constructor();
71
72        Ok(plugin)
73    }
74}
75
76impl fmt::Debug for PluginProxy {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        f.debug_struct("PluginProxy").finish()
79    }
80}
81
82// Inspired by async-compat.
83static TOKIO: LazyLock<tokio::runtime::Runtime> = LazyLock::new(|| {
84    std::thread::Builder::new()
85        .name("openrr-plugin/tokio".to_owned())
86        .spawn(move || TOKIO.block_on(std::future::pending::<()>()))
87        .unwrap();
88    tokio::runtime::Builder::new_multi_thread()
89        .enable_all()
90        .build()
91        .expect("cannot start tokio runtime")
92});