use core::any::TypeId;
use core::marker::PhantomData;
use super::UserdataRegistry;
use super::registry::UserdataRegistryState;
#[doc(hidden)]
pub struct UserdataRegistration {
type_id: fn() -> TypeId,
file: &'static str,
line: u32,
column: u32,
register: for<'lua> fn(&mut UserdataMacroRegistry<'lua>),
}
inventory::collect!(UserdataRegistration);
impl UserdataRegistration {
#[doc(hidden)]
pub const fn new<T: 'static>(
file: &'static str,
line: u32,
column: u32,
register: for<'lua> fn(&mut UserdataMacroRegistry<'lua>),
) -> Self {
Self {
type_id: userdata_type_id::<T>,
file,
line,
column,
register,
}
}
}
#[doc(hidden)]
pub struct UserdataMacroRegistry<'lua> {
type_id: TypeId,
state: Option<UserdataRegistryState<'lua>>,
}
impl<'lua> UserdataMacroRegistry<'lua> {
#[doc(hidden)]
pub fn with_type<T: 'static>(&mut self, register: impl FnOnce(&mut UserdataRegistry<'lua, T>)) {
assert_eq!(
self.type_id,
TypeId::of::<T>(),
"userdata registration descriptor used with the wrong type"
);
let mut registry = UserdataRegistry {
state: self.state.take(),
_marker: PhantomData,
};
register(&mut registry);
self.state = registry.state.take();
}
}
fn userdata_type_id<T: 'static>() -> TypeId {
TypeId::of::<T>()
}
#[doc(hidden)]
pub fn register_userdata_impls<T: 'static>(registry: &mut UserdataRegistry<'_, T>) {
let type_id = TypeId::of::<T>();
let mut registrations: Vec<_> = inventory::iter::<UserdataRegistration>
.into_iter()
.filter(|registration| (registration.type_id)() == type_id)
.collect();
registrations.sort_unstable_by(|left, right| {
(left.file, left.line, left.column).cmp(&(right.file, right.line, right.column))
});
if registrations.is_empty() {
return;
}
let mut erased = UserdataMacroRegistry {
type_id,
state: registry.state.take(),
};
for registration in registrations {
(registration.register)(&mut erased);
}
registry.state = erased.state.take();
}