use std::collections::HashMap;
use std::sync::OnceLock;
use parking_lot::Mutex;
use crate::types::{KdcFreeResponsePtr, KdcRequestHandlerCbFunc};
struct HandlerEntry {
handler: KdcRequestHandlerCbFunc,
free: KdcFreeResponsePtr,
}
static HANDLERS: OnceLock<Mutex<HashMap<u32, HandlerEntry>>> = OnceLock::new();
fn get_registry() -> &'static Mutex<HashMap<u32, HandlerEntry>> {
HANDLERS.get_or_init(|| Mutex::new(HashMap::new()))
}
#[no_mangle]
pub extern "C" fn register_handler(
msg_type: u32,
handler: KdcRequestHandlerCbFunc,
free: KdcFreeResponsePtr,
) -> u32 {
let registry = get_registry();
let mut map = registry.lock();
if map.contains_key(&msg_type) {
log::warn!("handler registration rejected: msg_type {} already registered", msg_type);
return 1;
}
map.insert(msg_type, HandlerEntry { handler, free });
0
}
#[no_mangle]
pub extern "C" fn unregister_handler(msg_type: u32) -> u32 {
let registry = get_registry();
let mut map = registry.lock();
if map.remove(&msg_type).is_some() {
0
} else {
log::warn!("handler unregistration skipped: msg_type {} not found", msg_type);
1
}
}
pub fn get_handler(msg_type: u32) -> Option<(KdcRequestHandlerCbFunc, KdcFreeResponsePtr)> {
let registry = get_registry();
let map = registry.lock();
map.get(&msg_type).map(|entry| (entry.handler, entry.free))
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{KdcRequestMsg, KdcResponseMsg};
extern "C" fn dummy_handler_a(_req: *const KdcRequestMsg, _res: *mut KdcResponseMsg) -> u32 {
0
}
extern "C" fn dummy_handler_b(_req: *const KdcRequestMsg, _res: *mut KdcResponseMsg) -> u32 {
1
}
extern "C" fn noop_free(_res: *mut KdcResponseMsg) {}
#[test]
fn test_register_and_get_handler() {
let _ = unregister_handler(1);
let result = register_handler(1, dummy_handler_a, noop_free);
assert_eq!(result, 0);
let handler = get_handler(1);
assert!(handler.is_some());
let _ = unregister_handler(1);
}
#[test]
fn test_register_duplicate_returns_error() {
let _ = unregister_handler(100);
let result = register_handler(100, dummy_handler_a, noop_free);
assert_eq!(result, 0);
let result = register_handler(100, dummy_handler_b, noop_free);
assert_eq!(result, 1);
let _ = unregister_handler(100);
}
#[test]
fn test_unregister_nonexistent_returns_error() {
let result = unregister_handler(999);
assert_eq!(result, 1);
}
#[test]
fn test_get_handler_unregistered_returns_none() {
let _ = unregister_handler(2);
assert!(get_handler(2).is_none());
}
#[test]
fn test_unregister_clears_handler() {
let _ = unregister_handler(400);
let _ = register_handler(400, dummy_handler_a, noop_free);
assert!(get_handler(400).is_some());
let result = unregister_handler(400);
assert_eq!(result, 0);
assert!(get_handler(400).is_none());
}
#[test]
fn test_register_second_handler_independent() {
let _ = unregister_handler(500);
let _ = unregister_handler(501);
let r1 = register_handler(500, dummy_handler_a, noop_free);
let r2 = register_handler(501, dummy_handler_b, noop_free);
assert_eq!(r1, 0);
assert_eq!(r2, 0);
assert!(get_handler(500).is_some());
assert!(get_handler(501).is_some());
let _ = unregister_handler(500);
let _ = unregister_handler(501);
}
#[test]
fn test_reregister_after_unregister() {
let _ = unregister_handler(600);
assert_eq!(register_handler(600, dummy_handler_a, noop_free), 0);
assert_eq!(unregister_handler(600), 0);
assert_eq!(register_handler(600, dummy_handler_b, noop_free), 0);
let _ = unregister_handler(600);
}
}