#[cfg(feature = "std")]
extern crate std;
extern crate alloc;
use alloc::{boxed::Box, vec::Vec};
use std::sync::LazyLock;
use std::sync::Mutex;
struct OsApiHook(Box<dyn FnOnce(&mut flecs_ecs::sys::ecs_os_api_t)>);
unsafe impl Send for OsApiHook {}
static OS_API_HOOKS: LazyLock<Mutex<Option<Vec<OsApiHook>>>> =
LazyLock::new(|| Mutex::new(Some(Default::default())));
pub fn ensure_initialized() {
let Some(hooks) = OS_API_HOOKS
.lock()
.expect("Internal OS API hook list lock should not be poisoned")
.take()
else {
return;
};
let mut api = unsafe {
flecs_ecs::sys::ecs_os_set_api_defaults();
flecs_ecs::sys::ecs_os_get_api()
};
for h in hooks {
(h.0)(&mut api);
}
unsafe {
flecs_ecs::sys::ecs_os_set_api(&mut api as *mut _);
};
}
pub fn add_init_hook(f: Box<dyn FnOnce(&mut flecs_ecs::sys::ecs_os_api_t)>) {
if let Err(e) = try_add_init_hook(f) {
panic!("{e}");
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum AddInitHookError {
LockPoisoned,
AlreadyInitialized,
}
impl core::fmt::Display for AddInitHookError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
AddInitHookError::LockPoisoned => {
write!(f, "Internal Flecs OS API hook list lock was poisoned")
}
AddInitHookError::AlreadyInitialized => write!(
f,
"Flecs OS API has already been initialized, adding hooks will have no effect now"
),
}
}
}
impl core::error::Error for AddInitHookError {}
pub fn try_add_init_hook(
f: Box<dyn FnOnce(&mut flecs_ecs::sys::ecs_os_api_t)>,
) -> Result<(), AddInitHookError> {
OS_API_HOOKS
.lock()
.map_err(|_| AddInitHookError::LockPoisoned)
.and_then(|mut h| {
h.as_mut()
.map(|h| h.push(OsApiHook(f)))
.ok_or(AddInitHookError::AlreadyInitialized)
})
}