1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
//! Internal implementation details for the host-guest interface.
//!
//! Note that the vast majority of this module is just providing FFI-safe
//! versions of common `std` types (e.g. `Vec`, `String`, and `Box<dyn Error>`),
//! or FFI-safe trait objects.
//!
/// If the macro generated the wrong code, this doctest would fail.
///
/// ```rust
/// use fj::{abi::INIT_FUNCTION_NAME, models::Metadata};
///
/// fj::register_model!(|_| {
/// Ok(Metadata::new("My Model", "1.2.3"))
/// });
///
/// mod x {
/// extern "C" {
/// pub fn fj_model_init(_: *mut fj::abi::Host<'_>) -> fj::abi::InitResult;
/// }
/// }
///
/// // make sure our function has the right signature
/// let func: fj::abi::InitFunction = fj_model_init;
///
/// // We can also make sure the unmangled name is correct by calling it.
///
/// let metadata: fj::models::Metadata = unsafe {
/// let mut host = Host;
/// let mut host = fj::abi::Host::from(&mut host);
/// x::fj_model_init(&mut host).unwrap().into()
/// };
///
/// assert_eq!(metadata.name, "My Model");
///
/// struct Host;
/// impl fj::models::Host for Host {
/// fn register_boxed_model(&mut self, model: Box<dyn fj::models::Model>) { todo!() }
/// }
/// ```
mod context;
pub mod ffi_safe;
mod host;
mod metadata;
mod model;
use std::any::Any;
pub use self::{
context::Context,
host::Host,
metadata::{Metadata, ModelMetadata},
model::Model,
};
/// Define the initialization routine used when registering models.
///
/// See the [`crate::model`] macro if your model can be implemented as a pure
/// function.
///
/// # Examples
///
/// ```rust
/// use fj::models::*;
///
/// fj::register_model!(|host: &mut dyn Host| {
/// host.register_model(MyModel::default());
///
/// Ok(Metadata::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")))
/// });
///
/// #[derive(Default)]
/// struct MyModel { }
///
/// impl Model for MyModel {
/// fn metadata(&self) -> std::result::Result<fj::models::ModelMetadata, Box<(dyn std::error::Error + Send + Sync + 'static)>> { todo!() }
///
/// fn shape(&self, ctx: &dyn Context) -> Result<fj::Shape, Error> {
/// todo!()
/// }
/// }
/// ```
#[macro_export]
macro_rules! register_model {
($init:expr) => {
#[no_mangle]
unsafe extern "C" fn fj_model_init(
mut host: *mut $crate::abi::Host<'_>,
) -> $crate::abi::InitResult {
let init: fn(
&mut dyn $crate::models::Host,
) -> Result<
$crate::models::Metadata,
$crate::models::Error,
> = $init;
match init(&mut *host) {
Ok(meta) => $crate::abi::InitResult::Ok(meta.into()),
Err(e) => $crate::abi::InitResult::Err(e.into()),
}
}
};
}
/// The signature of the function generated by [`register_model`].
///
/// ```rust
/// fj::register_model!(|_| { todo!() });
///
/// const _: fj::abi::InitFunction = fj_model_init;
/// ```
pub type InitFunction = unsafe extern "C" fn(*mut Host<'_>) -> InitResult;
pub type InitResult = ffi_safe::Result<Metadata, ffi_safe::BoxedError>;
pub type ShapeResult = ffi_safe::Result<crate::Shape, ffi_safe::BoxedError>;
pub type ModelMetadataResult =
ffi_safe::Result<ModelMetadata, ffi_safe::BoxedError>;
/// The name of the function generated by [`register_model`].
///
pub const INIT_FUNCTION_NAME: &str = "fj_model_init";
fn on_panic(payload: Box<dyn Any + Send>) -> crate::abi::ffi_safe::String {
let msg: &str =
if let Some(s) = payload.downcast_ref::<std::string::String>() {
s.as_str()
} else if let Some(s) = payload.downcast_ref::<&str>() {
s
} else {
"A panic occurred"
};
crate::abi::ffi_safe::String::from(msg.to_string())
}