rich_ext/plugin_loading/
native.rs1#![allow(unsafe_code)]
12
13use std::path::Path;
14use std::sync::Arc;
15
16use rich_plugin_api::abi::{
17 AbiOutput, AbiStr, DYLIB_ENTRY_SYMBOL, PluginDescriptor, PluginFunctions, STATUS_OK,
18 read_descriptor,
19};
20
21use super::{AbiBackend, LoadError, MAX_OUTPUT_BYTES, RuntimeKind, RuntimePlugin};
22
23struct NativeBackend {
26 vtable: PluginFunctions,
27 _library: libloading::Library,
28}
29
30impl AbiBackend for NativeBackend {
31 fn call(&self, capability: usize, input: &str, width: u32) -> Result<String, String> {
32 let mut output = AbiOutput::empty();
33 let status =
36 unsafe { (self.vtable.call)(capability, AbiStr::new(input), width, &mut output) };
37 let text = if output.ptr.is_null() {
38 Err("the plugin returned no output".to_string())
39 } else if output.len > MAX_OUTPUT_BYTES {
40 Err(format!(
41 "the plugin returned {} bytes; at most {MAX_OUTPUT_BYTES} are accepted",
42 output.len
43 ))
44 } else {
45 let bytes = unsafe { std::slice::from_raw_parts(output.ptr, output.len) };
47 String::from_utf8(bytes.to_vec()).map_err(|_| "the plugin returned non-UTF-8".into())
48 };
49 if !output.ptr.is_null() {
50 unsafe { (self.vtable.free)(output) };
52 }
53 match (status, text) {
54 (STATUS_OK, text) => text,
55 (_, Ok(message)) => Err(message),
56 (_, Err(error)) => Err(error),
57 }
58 }
59}
60
61pub fn load_native(path: &Path) -> Result<RuntimePlugin, LoadError> {
65 let path = std::fs::canonicalize(path).map_err(|error| LoadError::Io {
68 path: path.to_path_buf(),
69 message: error.to_string(),
70 })?;
71 let library =
76 unsafe { libloading::Library::new(&path) }.map_err(|error| LoadError::NotAPlugin {
77 path: path.clone(),
78 message: error_chain(&error),
79 })?;
80 let descriptor = {
81 let entry: libloading::Symbol<unsafe extern "C" fn() -> *const PluginDescriptor> = unsafe {
83 library.get(DYLIB_ENTRY_SYMBOL.as_bytes())
84 }
85 .map_err(|_| LoadError::NotAPlugin {
86 path: path.clone(),
87 message: format!("it does not export {DYLIB_ENTRY_SYMBOL}"),
88 })?;
89 unsafe { entry() }
91 };
92 let (abi, vtable) = unsafe { read_descriptor(descriptor) }.map_err(|error| LoadError::Abi {
94 path: path.clone(),
95 error,
96 })?;
97 let backend = NativeBackend {
98 vtable,
99 _library: library,
100 };
101 RuntimePlugin::new(abi, RuntimeKind::Native, path, Arc::new(backend))
102}
103
104fn error_chain(error: &dyn std::error::Error) -> String {
107 let mut message = error.to_string();
108 let mut source = error.source();
109 while let Some(cause) = source {
110 let text = cause.to_string();
111 if !message.contains(&text) {
112 message.push_str(": ");
113 message.push_str(&text);
114 }
115 source = cause.source();
116 }
117 message
118}