PkmnLib_rs/src/ffi/mod.rs

167 lines
5.1 KiB
Rust

/// The foreign function interfaces for the dynamic data
mod dynamic_data;
mod ffi_handle;
/// The foreign function interfaces for that static data
mod static_data;
pub(self) use ffi_handle::*;
/// Helper type for clearer functions.
type OwnedPtrString = *mut c_char;
/// Helper type for clearer functions.
type NonOwnedPtrString = *const c_char;
/// Generates a basic getter foreign function interface.
macro_rules! ffi_handle_arc_dyn_getter {
(
$type:ty, $func:ident, $returns: ty
) => {
paste::paste! {
#[no_mangle]
extern "C" fn [< $type:snake _ $func >](ptr: FFIHandle<Arc<dyn $type>>) -> $returns {
ptr.from_ffi_handle().$func()
}
}
};
}
/// Generates a basic getter foreign function interface where the return type is a [`crate::StringKey`].
macro_rules! ffi_handle_arc_stringkey_getter {
(
$type:ty, $func:ident
) => {
paste::paste! {
#[no_mangle]
extern "C" fn [< $type:lower _ $func >](ptr: FFIHandle<Arc<dyn $type>>) -> NonOwnedPtrString {
std::ffi::CString::new(ptr.from_ffi_handle().$func().str()).unwrap().into_raw()
}
}
};
}
/// Generates a foreign function interface for a vec. This generates a length function, and a getter.
macro_rules! ffi_handle_vec_value_getters {
(
$name:ident, $type:ty, $func:ident, $returns: ty
) => {
paste::paste! {
#[no_mangle]
extern "C" fn [< $name:lower _ $func _length>](ptr: FFIHandle<Arc<$type>>) -> usize {
ptr.from_ffi_handle().$func().len()
}
#[no_mangle]
extern "C" fn [< $name:lower _ $func _get>](ptr: FFIHandle<Arc<$type>>, index: usize) -> $returns {
*ptr.from_ffi_handle().$func().get(index).unwrap()
}
}
};
}
/// Generates a foreign function interface for a vec of [`crate::StringKey`]. This generates a
/// length function, and a getter.
macro_rules! ffi_handle_vec_stringkey_getters {
(
$type:ty, $func:ident
) => {
paste::paste! {
#[no_mangle]
extern "C" fn [< $type:lower _ $func _length>](ptr: FFIHandle<Arc<dyn $type>>) -> usize {
ptr.from_ffi_handle().$func().len()
}
#[no_mangle]
extern "C" fn [< $type:lower _ $func _get>](ptr: FFIHandle<Arc<dyn $type>>, index: usize) -> OwnedPtrString {
CString::new(ptr.from_ffi_handle().$func().get(index).unwrap().str()).unwrap().into_raw()
}
}
};
}
pub(self) use ffi_handle_arc_dyn_getter;
pub(self) use ffi_handle_arc_stringkey_getter;
pub(self) use ffi_handle_vec_stringkey_getters;
pub(self) use ffi_handle_vec_value_getters;
use std::ffi::{c_char, CString};
/// Helper utility class to wrap a pointer for extern functions.
#[repr(C)]
pub(self) struct ExternPointer<T: ?Sized> {
/// The wrapped pointer.
ptr: *mut T,
}
impl<T: ?Sized> ExternPointer<T> {
/// Get the internal pointer as reference.
#[allow(clippy::panic)] // We currently allow this as these should never be null, but we might want to change this in the future.
pub(self) fn as_ref(&self) -> &T {
unsafe {
self.ptr
.as_ref()
.unwrap_or_else(|| panic!("Given pointer of type '{}' was null", std::any::type_name::<T>()))
}
}
}
/// Helper utility class to give either the data or an error to a FFI.
#[repr(C)]
union ResultUnion<T: Copy> {
/// If the result is ok, this contains the value.
ok: T,
/// If the result is an error, this contains the error message.
err: NonOwnedPtrString,
}
/// The result of a FFI call that can either be an error or a value.
#[repr(C)]
pub struct FFIResult<T: Copy> {
/// If the result is ok, this is 1, otherwise 0.
ok: u8,
/// The value or error.
value: ResultUnion<T>,
}
impl<T: Copy> FFIResult<T> {
/// Creates a new NativeResult with the given value.
pub fn ok(value: T) -> Self {
Self {
ok: 1,
value: ResultUnion { ok: value },
}
}
/// Creates a new NativeResult with the given error.
#[allow(clippy::unwrap_used)] // We know for certain this is not empty.
pub fn err(err: anyhow_ext::Error) -> Self {
Self {
ok: 0,
value: ResultUnion {
err: CString::new(err.to_string()).unwrap().into_raw(),
},
}
}
/// Creates a new NativeResult with the given error string.
#[allow(clippy::unwrap_used)] // We know for certain this is not empty.
pub fn err_from_str(err: &str) -> Self {
Self {
ok: 0,
value: ResultUnion {
err: CString::new(err).unwrap().into_raw(),
},
}
}
}
impl<T: Copy> From<anyhow::Result<T>> for FFIResult<T> {
fn from(value: anyhow::Result<T>) -> Self {
match value {
Ok(v) => Self::ok(v),
Err(e) => Self::err(e),
}
}
}
impl<T: Copy> From<T> for FFIResult<T> {
fn from(value: T) -> Self {
Self::ok(value)
}
}