use std::ffi::{CStr, c_void};
pub(crate) trait SdkLibrary {
fn symbol(&self, name: &CStr) -> Option<*const c_void>;
}
pub(crate) unsafe fn entry_point<F: Copy>(library: &impl SdkLibrary, name: &CStr) -> Option<F> {
const {
assert!(size_of::<F>() == size_of::<*const c_void>());
}
let address = library.symbol(name).filter(|p| !p.is_null())?;
Some(unsafe { std::mem::transmute_copy::<*const c_void, F>(&address) })
}
#[cfg(test)]
mod tests {
use super::*;
struct Exports;
extern "C" fn answer() -> i32 {
42
}
impl SdkLibrary for Exports {
fn symbol(&self, name: &CStr) -> Option<*const c_void> {
match name.to_bytes() {
b"answer" => Some(answer as *const c_void),
b"null" => Some(std::ptr::null()),
_ => None,
}
}
}
#[test]
fn resolves_present_exports_and_rejects_missing_or_null_ones() {
let f = unsafe { entry_point::<extern "C" fn() -> i32>(&Exports, c"answer") };
assert_eq!(f.map(|f| f()), Some(42));
unsafe {
assert!(entry_point::<extern "C" fn() -> i32>(&Exports, c"missing").is_none());
assert!(entry_point::<extern "C" fn() -> i32>(&Exports, c"null").is_none());
}
}
}