use super::{EventArgs, EventHandler, EventResult, EventType, HandlerEntry};
use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
#[deprecated(
since = "0.1.0",
note = "Use Bevy ECS events (scarab-client/src/events/bevy_events.rs) instead of Arc<Mutex<EventRegistry>>. \
See crates/scarab-plugin-api/src/events/registry.rs module docs for migration guide."
)]
pub struct EventRegistry {
handlers: HashMap<EventType, Vec<HandlerEntry>>,
custom_handlers: HashMap<String, Vec<HandlerEntry>>,
next_handler_id: AtomicU64,
}
#[allow(deprecated)]
impl EventRegistry {
pub fn new() -> Self {
Self {
handlers: HashMap::new(),
custom_handlers: HashMap::new(),
next_handler_id: AtomicU64::new(1),
}
}
pub fn register(
&mut self,
event_type: EventType,
priority: i32,
plugin_name: &str,
handler: EventHandler,
) -> u64 {
let id = self.next_handler_id.fetch_add(1, Ordering::SeqCst);
let entry = HandlerEntry::new(id, plugin_name, priority, handler);
match event_type {
EventType::Custom(ref name) => {
let name = name.clone();
let handlers = self.custom_handlers.entry(name).or_insert_with(Vec::new);
handlers.push(entry);
handlers.sort_by(|a, b| b.priority.cmp(&a.priority));
}
_ => {
let handlers = self.handlers.entry(event_type).or_insert_with(Vec::new);
handlers.push(entry);
handlers.sort_by(|a, b| b.priority.cmp(&a.priority));
}
}
id
}
pub fn unregister(&mut self, handler_id: u64) -> bool {
for handlers in self.handlers.values_mut() {
if let Some(pos) = handlers.iter().position(|h| h.id == handler_id) {
handlers.remove(pos);
return true;
}
}
for handlers in self.custom_handlers.values_mut() {
if let Some(pos) = handlers.iter().position(|h| h.id == handler_id) {
handlers.remove(pos);
return true;
}
}
false
}
pub fn unregister_plugin(&mut self, plugin_name: &str) -> usize {
let mut count = 0;
for handlers in self.handlers.values_mut() {
let before = handlers.len();
handlers.retain(|h| h.plugin_name != plugin_name);
count += before - handlers.len();
}
for handlers in self.custom_handlers.values_mut() {
let before = handlers.len();
handlers.retain(|h| h.plugin_name != plugin_name);
count += before - handlers.len();
}
count
}
pub fn dispatch(&self, args: &EventArgs) -> Vec<EventResult> {
let handlers = match &args.event_type {
EventType::Custom(name) => self.custom_handlers.get(name).map(|h| h.as_slice()),
_ => self.handlers.get(&args.event_type).map(|h| h.as_slice()),
};
let Some(handlers) = handlers else {
return Vec::new();
};
let mut results = Vec::with_capacity(handlers.len());
for handler_entry in handlers {
let result = handler_entry.call(args);
let should_stop = result.is_stop();
results.push(result);
if should_stop {
break;
}
}
results
}
pub fn get_handlers(&self, event_type: &EventType) -> &[HandlerEntry] {
match event_type {
EventType::Custom(name) => self
.custom_handlers
.get(name)
.map(|h| h.as_slice())
.unwrap_or(&[]),
_ => self
.handlers
.get(event_type)
.map(|h| h.as_slice())
.unwrap_or(&[]),
}
}
pub fn handler_count(&self, event_type: &EventType) -> usize {
self.get_handlers(event_type).len()
}
pub fn total_handler_count(&self) -> usize {
let standard_count: usize = self.handlers.values().map(|v| v.len()).sum();
let custom_count: usize = self.custom_handlers.values().map(|v| v.len()).sum();
standard_count + custom_count
}
pub fn clear_event(&mut self, event_type: &EventType) {
match event_type {
EventType::Custom(name) => {
self.custom_handlers.remove(name);
}
_ => {
self.handlers.remove(event_type);
}
}
}
pub fn clear_all(&mut self) {
self.handlers.clear();
self.custom_handlers.clear();
}
pub fn registered_events(&self) -> Vec<EventType> {
let mut events: Vec<EventType> = self.handlers.keys().cloned().collect();
for custom_name in self.custom_handlers.keys() {
events.push(EventType::Custom(custom_name.clone()));
}
events
}
}
#[allow(deprecated)]
impl Default for EventRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Arc, Mutex};
#[test]
fn test_registry_creation() {
let registry = EventRegistry::new();
assert_eq!(registry.total_handler_count(), 0);
}
#[test]
fn test_register_and_dispatch() {
let mut registry = EventRegistry::new();
let called = Arc::new(Mutex::new(false));
let called_clone = Arc::clone(&called);
let id = registry.register(
EventType::Bell,
100,
"test-plugin",
Box::new(move |_| {
*called_clone.lock().unwrap() = true;
EventResult::Continue
}),
);
assert_eq!(id, 1);
assert_eq!(registry.handler_count(&EventType::Bell), 1);
let args = EventArgs::new(EventType::Bell);
let results = registry.dispatch(&args);
assert_eq!(results.len(), 1);
assert!(results[0].is_continue());
assert!(*called.lock().unwrap());
}
#[test]
fn test_priority_ordering() {
let mut registry = EventRegistry::new();
let order = Arc::new(Mutex::new(Vec::new()));
for (i, priority) in [10, 50, 30].iter().enumerate() {
let order_clone = Arc::clone(&order);
registry.register(
EventType::Bell,
*priority,
&format!("plugin-{}", i),
Box::new(move |_| {
order_clone.lock().unwrap().push(*priority);
EventResult::Continue
}),
);
}
let args = EventArgs::new(EventType::Bell);
registry.dispatch(&args);
let order = order.lock().unwrap();
assert_eq!(*order, vec![50, 30, 10]);
}
#[test]
fn test_stop_processing() {
let mut registry = EventRegistry::new();
let call_count = Arc::new(Mutex::new(0));
for i in 0..3 {
let call_count_clone = Arc::clone(&call_count);
registry.register(
EventType::Bell,
100 - i,
&format!("plugin-{}", i),
Box::new(move |_| {
*call_count_clone.lock().unwrap() += 1;
if i == 1 {
EventResult::Stop
} else {
EventResult::Continue
}
}),
);
}
let args = EventArgs::new(EventType::Bell);
let results = registry.dispatch(&args);
assert_eq!(*call_count.lock().unwrap(), 2);
assert_eq!(results.len(), 2);
assert!(results[1].is_stop());
}
#[test]
fn test_unregister() {
let mut registry = EventRegistry::new();
let id = registry.register(
EventType::Bell,
100,
"test",
Box::new(|_| EventResult::Continue),
);
assert_eq!(registry.handler_count(&EventType::Bell), 1);
assert!(registry.unregister(id));
assert_eq!(registry.handler_count(&EventType::Bell), 0);
assert!(!registry.unregister(id)); }
#[test]
fn test_unregister_plugin() {
let mut registry = EventRegistry::new();
registry.register(
EventType::Bell,
100,
"plugin-a",
Box::new(|_| EventResult::Continue),
);
registry.register(
EventType::Bell,
90,
"plugin-b",
Box::new(|_| EventResult::Continue),
);
registry.register(
EventType::TabCreated,
100,
"plugin-a",
Box::new(|_| EventResult::Continue),
);
assert_eq!(registry.total_handler_count(), 3);
let removed = registry.unregister_plugin("plugin-a");
assert_eq!(removed, 2);
assert_eq!(registry.total_handler_count(), 1);
}
#[test]
fn test_custom_events() {
let mut registry = EventRegistry::new();
let called = Arc::new(Mutex::new(false));
let called_clone = Arc::clone(&called);
registry.register(
EventType::Custom("my-event".to_string()),
100,
"test",
Box::new(move |_| {
*called_clone.lock().unwrap() = true;
EventResult::Continue
}),
);
let args = EventArgs::new(EventType::Custom("my-event".to_string()));
registry.dispatch(&args);
assert!(*called.lock().unwrap());
}
#[test]
fn test_clear_operations() {
let mut registry = EventRegistry::new();
registry.register(
EventType::Bell,
100,
"test",
Box::new(|_| EventResult::Continue),
);
registry.register(
EventType::TabCreated,
100,
"test",
Box::new(|_| EventResult::Continue),
);
assert_eq!(registry.total_handler_count(), 2);
registry.clear_event(&EventType::Bell);
assert_eq!(registry.total_handler_count(), 1);
registry.clear_all();
assert_eq!(registry.total_handler_count(), 0);
}
#[test]
fn test_registered_events() {
let mut registry = EventRegistry::new();
registry.register(
EventType::Bell,
100,
"test",
Box::new(|_| EventResult::Continue),
);
registry.register(
EventType::Custom("test".to_string()),
100,
"test",
Box::new(|_| EventResult::Continue),
);
let events = registry.registered_events();
assert_eq!(events.len(), 2);
assert!(events.contains(&EventType::Bell));
assert!(events.contains(&EventType::Custom("test".to_string())));
}
#[test]
fn test_modified_result() {
let mut registry = EventRegistry::new();
registry.register(
EventType::Output,
100,
"test",
Box::new(|_| EventResult::Modified(vec![1, 2, 3])),
);
let args = EventArgs::new(EventType::Output);
let results = registry.dispatch(&args);
assert_eq!(results.len(), 1);
assert_eq!(results[0].as_modified(), Some(&[1, 2, 3][..]));
}
}