| Crates.io | orceh |
| lib.rs | orceh |
| version | 0.1.1 |
| created_at | 2025-11-04 17:07:32.84594+00 |
| updated_at | 2025-11-04 17:10:13.170582+00 |
| description | Easier plugin interfaces |
| homepage | |
| repository | |
| max_upload_size | |
| id | 1916588 |
| size | 33,274 |
orceh is a rust crate focused on making it easier to write applications that have an ABI based plugin interface. It equates the concept of a Trait in rust, with the concept of a Plugin, or dynamically loaded library.
You can install orceh from crates-io (not yet).
cargo add orceh
The core API is the dylib macro. The macros creates a struct for loading a dynamic library that exposes the functions of the trait in it's ABI.
#[orceh::dylib]
pub trait Step {
fn on_init(handle: &mut Context) -> u32;
fn on_update(handle: &mut Context) -> u32;
fn on_destroy(handle: &mut Context) -> u32;
}
will generate:
#[doc = r" We permit these to be unused, because they are only used"]
#[doc = r" to generate the header files for the SDK"]
#[allow(unused)]
unsafe extern "C" {
fn on_init(handle: &mut Context) -> u32;
fn on_update(handle: &mut Context) -> u32;
fn on_destroy(handle: &mut Context) -> u32;
}
pub struct StepPluginHandle {
handle: ::orceh::_exports::libloading::Library,
}
impl StepPluginHandle {
pub fn init(path: &std::path::Path) -> Result<Self, ::orceh::Error> {
let handle = unsafe { ::orceh::_exports::libloading::Library::new(path) }
.map_err(|_| ::orceh::Error::LibraryNotFound(path.to_owned()))?;
let maybe_func: Option<
::orceh::_exports::libloading::Symbol<unsafe extern "C" fn(handle: &mut Context) -> u32>,
> = unsafe { handle.get("on_init".as_bytes()) }.ok();
if maybe_func.is_none() {
return Err(::orceh::Error::SymbolNotFound(
path.to_owned(),
"on_init".into(),
));
}
let maybe_func: Option<
::orceh::_exports::libloading::Symbol<unsafe extern "C" fn(handle: &mut Context) -> u32>,
> = unsafe { handle.get("on_update".as_bytes()) }.ok();
if maybe_func.is_none() {
return Err(::orceh::Error::SymbolNotFound(
path.to_owned(),
"on_update".into(),
));
}
let maybe_func: Option<
::orceh::_exports::libloading::Symbol<unsafe extern "C" fn(handle: &mut Context) -> u32>,
> = unsafe { handle.get("on_destroy".as_bytes()) }.ok();
if maybe_func.is_none() {
return Err(::orceh::Error::SymbolNotFound(
path.to_owned(),
"on_destroy".into(),
));
}
Ok(Self { handle })
}
pub fn on_init(&self, handle: &mut Context) -> u32 {
let func: ::orceh::_exports::libloading::Symbol<
unsafe extern "C" fn(handle: &mut Context) -> u32,
> = unsafe { self.handle.get("on_init".as_bytes()) }.unwrap();
unsafe { func(handle) }
}
pub fn on_update(&self, handle: &mut Context) -> u32 {
let func: ::orceh::_exports::libloading::Symbol<
unsafe extern "C" fn(handle: &mut Context) -> u32,
> = unsafe { self.handle.get("on_update".as_bytes()) }.unwrap();
unsafe { func(handle) }
}
pub fn on_destroy(&self, handle: &mut Context) -> u32 {
let func: ::orceh::_exports::libloading::Symbol<
unsafe extern "C" fn(handle: &mut Context) -> u32,
> = unsafe { self.handle.get("on_destroy".as_bytes()) }.unwrap();
unsafe { func(handle) }
}
}
The extern component at the top enabled using cbindgen to generate a header file that looks like:
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef void *Context;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
extern uint32_t on_init(Context *handle);
extern uint32_t on_update(Context *handle);
extern uint32_t on_destroy(Context *handle);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
while the rest enables a clean interface from rust for loading a dynamic library, and calling from rust.
pub fn main() {
let plugin = StepPluginHandle::init(&PathBuf::from("/usr/lib/foo.sh")).unwrap();
let mut c = Context::default();
let _res = plugin.on_init(&mut c);
let _res = plugin.on_update(&mut c);
let _res = plugin.on_destroy(&mut c);
}
It's not a lot, it's very simple. But it saves time, and energy from thinking about the libloading library.
You can look more into the examples for different plugin interfaces, as well as some implementations for the plugins.
The orceh project is open to contributions, and ideas for ways we can make it better, or add new features. Reach out to the maintainers.
Maintainer: benj2468
For open source projects, say how it is licensed.
In development.