use crate::language::syntax::ast::{Statement, Value};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct EventHandler {
pub event_name: String,
pub body: Vec<Statement>,
pub once: bool, pub args: Option<Vec<Value>>,
}
#[derive(Debug, Clone)]
pub struct EventPayload {
pub event_name: String,
pub data: HashMap<String, Value>,
pub timestamp: f32, }
#[derive(Debug, Clone, Default)]
pub struct EventRegistry {
handlers: HashMap<String, Vec<EventHandler>>,
emitted_events: Vec<EventPayload>,
executed_once: HashMap<String, usize>, }
impl EventRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register_handler(&mut self, handler: EventHandler) {
let event_name = handler.event_name.clone();
self.handlers
.entry(event_name)
.or_insert_with(Vec::new)
.push(handler);
}
pub fn emit(&mut self, event_name: String, data: HashMap<String, Value>, timestamp: f32) {
let payload = EventPayload {
event_name,
data,
timestamp,
};
self.emitted_events.push(payload);
}
pub fn get_handlers(&self, event_name: &str) -> Vec<EventHandler> {
self.handlers.get(event_name).cloned().unwrap_or_default()
}
pub fn get_handlers_matching(&self, pattern: &str) -> Vec<EventHandler> {
let mut matching = Vec::new();
for (event_name, handlers) in &self.handlers {
if pattern_matches(pattern, event_name) {
matching.extend(handlers.clone());
}
}
matching
}
pub fn should_execute_once(&mut self, event_name: &str, handler_index: usize) -> bool {
let key = format!("{}:{}", event_name, handler_index);
if self.executed_once.contains_key(&key) {
return false;
}
self.executed_once.insert(key, 1);
true
}
pub fn get_emitted_events(&self) -> &[EventPayload] {
&self.emitted_events
}
pub fn get_events_by_name(&self, event_name: &str) -> Vec<EventPayload> {
self.emitted_events
.iter()
.filter(|e| e.event_name == event_name)
.cloned()
.collect()
}
pub fn clear_events(&mut self) {
self.emitted_events.clear();
self.executed_once.clear();
}
pub fn handler_count(&self, event_name: &str) -> usize {
self.handlers.get(event_name).map(|h| h.len()).unwrap_or(0)
}
pub fn remove_handlers(&mut self, event_name: &str) {
self.handlers.remove(event_name);
}
}
fn pattern_matches(pattern: &str, event_name: &str) -> bool {
if pattern == "*" {
return true;
}
if !pattern.contains('*') && !pattern.contains('?') {
return pattern == event_name;
}
let mut pattern_chars = pattern.chars().peekable();
let mut name_chars = event_name.chars().peekable();
while pattern_chars.peek().is_some() || name_chars.peek().is_some() {
match pattern_chars.peek() {
Some('*') => {
pattern_chars.next();
if pattern_chars.peek().is_none() {
return true;
}
let remaining_pattern: String = pattern_chars.clone().collect();
while name_chars.peek().is_some() {
let remaining_name: String = name_chars.clone().collect();
if pattern_matches(&remaining_pattern, &remaining_name) {
return true;
}
name_chars.next();
}
return false;
}
Some('?') => {
pattern_chars.next();
if name_chars.next().is_none() {
return false;
}
}
Some(p) => {
let p = *p;
pattern_chars.next();
match name_chars.next() {
Some(n) if n == p => continue,
_ => return false,
}
}
None => {
return name_chars.peek().is_none();
}
}
}
true
}
pub mod builtin_events {
pub const BEAT: &str = "beat";
pub const BAR: &str = "bar";
pub const START: &str = "start";
pub const END: &str = "end";
pub const TEMPO_CHANGE: &str = "tempo.change";
pub const NOTE_ON: &str = "note.on";
pub const NOTE_OFF: &str = "note.off";
}
#[cfg(test)]
#[path = "test_events.rs"]
mod tests;