inputflow/
lib.rs

1pub mod api_traits;
2pub mod error;
3pub mod headers;
4pub mod key_types;
5
6use abi_stable::type_layout::TypeLayout;
7use abi_stable::StableAbi;
8use api_traits::{ControllerFeatures, Loadable};
9use cglue::prelude::v1::{trait_group::compare_layouts, *};
10use core::mem::MaybeUninit;
11use ::std::ffi::CString;
12use error::{InputFlowError, Result};
13use headers::PluginHeader;
14use libloading::{library_filename, Library, Symbol};
15
16#[cglue_trait]
17pub trait PluginInner<'a> {
18    #[wrap_with_group(ControllerFeatures)]
19    type BorrowedType: Loadable + 'a;
20    #[wrap_with_group(ControllerFeatures)]
21    type OwnedType: Loadable + 'static;
22    #[wrap_with_group_mut(ControllerFeatures)]
23    type OwnedTypeMut: Loadable + 'a;
24
25    fn borrow_features(&'a mut self) -> Self::BorrowedType;
26
27    fn into_features(self) -> Self::OwnedType;
28
29    fn mut_features(&'a mut self) -> &'a mut Self::OwnedTypeMut;
30}
31
32/// Having the inner type with a lifetime allows to borrow features for any lifetime.
33///
34/// This could be avoided with [GAT](https://rust-lang.github.io/rfcs/1598-generic_associated_types.html)
35pub trait Plugin: for<'a> PluginInner<'a> {}
36impl<T: for<'a> PluginInner<'a>> Plugin for T {}
37
38// pub type KeyValueCallback<'a> = OpaqueCallback<'a, KeyValue<'a>>;
39
40/// Load a plugin from a given library.
41///
42/// # Safety
43///
44/// Input library must implement a correct `create_plugin` and `get_root_layout()` functions.
45/// Its signatures must be as follows:
46///
47/// `extern "C" fn crate_plugin(&CArc<T>) -> PluginInnerArcBox<'static>`
48/// `extern "C" fn get_root_layout() -> Option<&'static TypeLayout>`
49///
50/// Where `T` is any type, since it's opaque. Meanwhile, `get_root_layout` should simply
51/// [call the one in this crate](self::get_root_layout). It is used to verify
52/// version mismatches.
53#[no_mangle]
54pub unsafe extern "C" fn load_plugin(
55    name: ReprCStr<'_>,
56    args: ReprCStr<'_>,
57    ok_out: &mut MaybeUninit<PluginInnerArcBox<'static>>,
58) -> i32 {
59    load_plugin_impl(name.as_ref(), args.as_ref()).into_int_out_result(ok_out)
60}
61
62unsafe fn load_plugin_impl(name: &str, args: &str) -> Result<PluginInnerArcBox<'static>> {
63    let mut current_exe = std::env::current_exe().map_err(|_| InputFlowError::Path)?;
64    current_exe.set_file_name(library_filename(name));
65    let lib = Library::new(current_exe).map_err(|e| {
66        println!("{}", e);
67        InputFlowError::Loading
68    })?;
69
70    let header: Symbol<&'static PluginHeader> = lib.get(b"IF_PLUGIN_HEAD\0").map_err(|e| {
71        println!("{}", e);
72        InputFlowError::Symbol
73    })?;
74    let header = header.into_raw();
75
76    if !compare_layouts(Some(ROOT_LAYOUT), Some(header.layout)).is_valid_strict() {
77        return Err(InputFlowError::Abi);
78    }
79
80    let arc = CArc::from(lib);
81    (header.create)(&arc.into_opaque(), CString::new(args).unwrap().into_raw())
82}
83
84/// Layout for the root vtable.
85///
86/// Layout that should be embedded to a `PluginHeader`.
87/// Other layouts are not necessary, because the very root depends on them already.
88#[no_mangle]
89pub static ROOT_LAYOUT: &TypeLayout = PluginInnerArcBox::LAYOUT;
90
91#[doc(hidden)]
92pub mod cglue {
93    pub use ::cglue::prelude::v1::*;
94}
95
96#[doc(hidden)]
97pub mod abi_stable {
98    pub use abi_stable::*;
99}
100
101#[doc(hidden)]
102#[allow(ambiguous_glob_reexports)]
103pub mod prelude {
104    pub mod v1 {
105        pub use crate::abi_stable;
106        pub use crate::api_traits::*;
107        pub use crate::cglue::*;
108        pub use crate::error::*;
109        pub use crate::headers::*;
110        pub use crate::iter::*;
111        pub use crate::key_types::*;
112        pub use crate::*;
113    }
114    pub use v1::*;
115}