yog_api/interop.rs
1//! Inter-mod communication — export/import function pointers between mods.
2//!
3//! Uses the runtime's global symbol table (C ABI). Mods export typed function
4//! pointers under `"symbol"` names; other mods import them by qualified name
5//! `"mod_id:symbol"`.
6//!
7//! ## Example
8//!
9//! ```ignore
10//! // Mod A exports:
11//! fn register_pipe_impl(api: *const YogApi, registry: &mut Registry, def: ...) { ... }
12//! registry.interop().export("register_pipe", register_pipe_impl as *const c_void);
13//!
14//! // Mod B imports:
15//! type RegisterPipeFn = unsafe extern "C" fn(api: *const YogApi, ...);
16//! let func: RegisterPipeFn = registry.interop().import("yog-pipes:register_pipe").unwrap();
17//! unsafe { func(api_ptr, ...) };
18//! ```
19
20use std::os::raw::c_void;
21
22/// Safe wrapper around the runtime's inter-mod symbol table.
23///
24/// Returned by [`Registry::interop`](crate::Registry::interop).
25pub struct Interop {
26 api: *const crate::YogApi,
27}
28
29impl Interop {
30 pub(crate) fn new(api: *const crate::YogApi) -> Self {
31 Interop { api }
32 }
33
34 /// Export a function pointer under `symbol` for the current mod.
35 ///
36 /// The mod's ID is automatically determined from the manifest (passed
37 /// to `yog_mod_register` by the runtime).
38 ///
39 /// # Safety
40 /// `ptr` must point to a valid function of the ABI expected by whoever
41 /// later imports and calls it under this `symbol`.
42 pub unsafe fn export(&self, symbol: &str, ptr: *const c_void) {
43 let mod_id = crate::__current_mod_id().unwrap_or_else(|| "unknown".into());
44 unsafe {
45 let api = &*self.api;
46 let mid = yog_abi::YogStr::from_str(&mod_id);
47 let sym = yog_abi::YogStr::from_str(symbol);
48 (api.interop_export)(api.ctx, mid, sym, ptr);
49 }
50 }
51
52 /// Import a function pointer exported by `mod_id` under `symbol`.
53 ///
54 /// Use the qualified form `"mod_id:symbol"`:
55 ///
56 /// ```ignore
57 /// let ptr: *const c_void = interop.import("yog-pipes:register_pipe").unwrap();
58 /// ```
59 ///
60 /// Returns `None` if the symbol has not been exported (yet).
61 ///
62 /// # Safety
63 /// The returned pointer, if any, must be cast back to the exact function
64 /// signature the exporting mod registered it under before being called.
65 pub unsafe fn import_raw(&self, mod_id: &str, symbol: &str) -> Option<*const c_void> {
66 unsafe {
67 let api = &*self.api;
68 let mid = yog_abi::YogStr::from_str(mod_id);
69 let sym = yog_abi::YogStr::from_str(symbol);
70 let ptr = (api.interop_import)(api.ctx, mid, sym);
71 if ptr.is_null() {
72 None
73 } else {
74 Some(ptr)
75 }
76 }
77 }
78
79 /// Convenience: parse `"mod_id:symbol"` and import.
80 ///
81 /// # Safety
82 /// Same requirement as [`Interop::import_raw`].
83 pub unsafe fn import(&self, qualified: &str) -> Option<*const c_void> {
84 let (mod_id, symbol) = qualified.split_once(':')?;
85 unsafe { self.import_raw(mod_id, symbol) }
86 }
87}