use crate::message::RingMessage;
pub const MAX_INLINE_PAYLOAD_SIZE: usize = 32;
pub mod message_flags {
pub const FLAG_EXTENDED: u32 = 0x01;
pub const FLAG_HIGH_PRIORITY: u32 = 0x02;
pub const FLAG_EXTERNAL_BUFFER: u32 = 0x04;
pub const FLAG_REQUIRES_RESPONSE: u32 = 0x08;
}
pub trait PersistentMessage: RingMessage + Sized {
fn handler_id() -> u32;
fn requires_response() -> bool {
false
}
fn to_inline_payload(&self) -> Option<[u8; MAX_INLINE_PAYLOAD_SIZE]>;
fn from_inline_payload(payload: &[u8]) -> crate::error::Result<Self>;
fn payload_size() -> usize;
fn can_inline() -> bool {
Self::payload_size() <= MAX_INLINE_PAYLOAD_SIZE
}
}
#[derive(Debug, Clone)]
pub struct HandlerRegistration {
pub handler_id: u32,
pub name: String,
pub message_type_id: u64,
pub produces_response: bool,
pub response_type_id: Option<u64>,
pub cuda_body: Option<String>,
}
impl HandlerRegistration {
pub fn new(handler_id: u32, name: impl Into<String>, message_type_id: u64) -> Self {
Self {
handler_id,
name: name.into(),
message_type_id,
produces_response: false,
response_type_id: None,
cuda_body: None,
}
}
pub fn with_response(mut self, response_type_id: u64) -> Self {
self.produces_response = true;
self.response_type_id = Some(response_type_id);
self
}
pub fn with_cuda_body(mut self, body: impl Into<String>) -> Self {
self.cuda_body = Some(body.into());
self
}
}
#[derive(Debug, Clone, Default)]
pub struct DispatchTable {
handlers: Vec<HandlerRegistration>,
max_handler_id: u32,
}
impl DispatchTable {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, registration: HandlerRegistration) -> crate::error::Result<()> {
if let Some(existing) = self
.handlers
.iter()
.find(|h| h.handler_id == registration.handler_id)
{
return Err(crate::error::RingKernelError::InvalidConfig(format!(
"Duplicate handler ID: {} (new: {}, existing: {})",
registration.handler_id, registration.name, existing.name
)));
}
self.max_handler_id = self.max_handler_id.max(registration.handler_id);
self.handlers.push(registration);
Ok(())
}
pub fn register_message<M: PersistentMessage>(
&mut self,
name: impl Into<String>,
) -> crate::error::Result<()> {
let registration = HandlerRegistration::new(M::handler_id(), name, M::message_type());
let registration = if M::requires_response() {
registration
} else {
registration
};
self.register(registration)
}
pub fn handlers(&self) -> &[HandlerRegistration] {
&self.handlers
}
pub fn get(&self, handler_id: u32) -> Option<&HandlerRegistration> {
self.handlers.iter().find(|h| h.handler_id == handler_id)
}
pub fn max_handler_id(&self) -> u32 {
self.max_handler_id
}
pub fn len(&self) -> usize {
self.handlers.len()
}
pub fn is_empty(&self) -> bool {
self.handlers.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dispatch_table_registration() {
let mut table = DispatchTable::new();
table
.register(HandlerRegistration::new(1, "fraud_check", 1001))
.unwrap();
table
.register(HandlerRegistration::new(2, "aggregate", 1002))
.unwrap();
table
.register(HandlerRegistration::new(3, "pattern_detect", 1003).with_response(2003))
.unwrap();
assert_eq!(table.len(), 3);
assert_eq!(table.max_handler_id(), 3);
let handler = table.get(2).unwrap();
assert_eq!(handler.name, "aggregate");
assert_eq!(handler.message_type_id, 1002);
assert!(!handler.produces_response);
let handler = table.get(3).unwrap();
assert!(handler.produces_response);
assert_eq!(handler.response_type_id, Some(2003));
}
#[test]
fn test_duplicate_handler_returns_error() {
let mut table = DispatchTable::new();
table
.register(HandlerRegistration::new(1, "first", 1001))
.unwrap();
let result = table.register(HandlerRegistration::new(1, "second", 1002));
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("Duplicate handler ID"));
}
#[test]
fn test_message_flags() {
assert_eq!(message_flags::FLAG_EXTENDED, 0x01);
assert_eq!(message_flags::FLAG_HIGH_PRIORITY, 0x02);
assert_eq!(message_flags::FLAG_EXTERNAL_BUFFER, 0x04);
assert_eq!(message_flags::FLAG_REQUIRES_RESPONSE, 0x08);
}
#[test]
fn test_max_inline_payload_size() {
assert_eq!(MAX_INLINE_PAYLOAD_SIZE, 32);
}
}