pub extern crate inventory;
pub struct EventPayloadRegistration {
pub kind_bits: u16,
pub type_name: &'static str,
}
inventory::collect!(EventPayloadRegistration);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct EventKindCollision {
pub kind_bits: u16,
pub first_type_name: &'static str,
pub second_type_name: &'static str,
}
pub fn find_kind_collisions() -> Vec<EventKindCollision> {
let mut seen: std::collections::HashMap<u16, &'static str> = std::collections::HashMap::new();
let mut collisions = Vec::new();
for item in inventory::iter::<EventPayloadRegistration>() {
if let Some(prior) = seen.insert(item.kind_bits, item.type_name) {
collisions.push(EventKindCollision {
kind_bits: item.kind_bits,
first_type_name: prior,
second_type_name: item.type_name,
});
}
}
collisions
}
pub fn assert_no_kind_collisions() {
if let Some(collision) = find_kind_collisions().into_iter().next() {
panic!(
"batpak EventKind collision detected.\n\
Types `{prior}` and `{ty}` share the same (category, type_id) \
pair (kind_bits = 0x{bits:04X}).\n\
Each EventPayload type must have a unique kind within a binary.",
prior = collision.first_type_name,
ty = collision.second_type_name,
bits = collision.kind_bits,
);
}
}
pub fn scan_for_kind_collisions() {
assert_no_kind_collisions();
}
pub const CATEGORY_MIN: u8 = 1;
pub const CATEGORY_MAX: u8 = 15;
pub const TYPE_ID_MAX: u16 = 0x0FFF;
pub fn validate_category(cat: u8) -> Result<(), &'static str> {
match cat {
0x0 => Err("category 0x0 is reserved for system events"),
0xD => Err("category 0xD is reserved for effect events"),
1..=15 => Ok(()),
_ => Err("category must fit in 4 bits (0x1–0xF, excluding 0x0 and 0xD)"),
}
}
pub fn validate_type_id(tid: u16) -> Result<(), &'static str> {
if tid > TYPE_ID_MAX {
Err("type_id must fit in 12 bits (0x000–0xFFF)")
} else {
Ok(())
}
}