orceh

Crates.ioorceh
lib.rsorceh
version0.1.1
created_at2025-11-04 17:07:32.84594+00
updated_at2025-11-04 17:10:13.170582+00
descriptionEasier plugin interfaces
homepage
repository
max_upload_size
id1916588
size33,274
Benjamin Cape (benj2468)

documentation

README

orceh

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.

Installation

You can install orceh from crates-io (not yet).

cargo add orceh

Usage

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.

Contributing

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.

Authors and acknowledgment

Maintainer: benj2468

License

For open source projects, say how it is licensed.

Project status

In development.

Commit count: 0

cargo fmt