devalang_wasm/engine/events/
mod.rs

1/// Event system for Devalang - "on" and "emit" statements
2/// Allows reactive programming and event-driven audio generation
3use crate::language::syntax::ast::{Statement, Value};
4use std::collections::HashMap;
5
6/// Event handler - function called when event is emitted
7#[derive(Debug, Clone)]
8pub struct EventHandler {
9    pub event_name: String,
10    pub body: Vec<Statement>,
11    pub once: bool, // If true, handler runs only once
12}
13
14/// Event payload - data associated with an emitted event
15#[derive(Debug, Clone)]
16pub struct EventPayload {
17    pub event_name: String,
18    pub data: HashMap<String, Value>,
19    pub timestamp: f32, // When the event was emitted
20}
21
22/// Event registry - manages event handlers and emitted events
23#[derive(Debug, Clone, Default)]
24pub struct EventRegistry {
25    handlers: HashMap<String, Vec<EventHandler>>,
26    emitted_events: Vec<EventPayload>,
27    executed_once: HashMap<String, usize>, // Track which handlers have executed (for "once")
28}
29
30impl EventRegistry {
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    /// Register an event handler
36    pub fn register_handler(&mut self, handler: EventHandler) {
37        let event_name = handler.event_name.clone();
38        self.handlers
39            .entry(event_name)
40            .or_insert_with(Vec::new)
41            .push(handler);
42    }
43
44    /// Emit an event with optional data
45    pub fn emit(&mut self, event_name: String, data: HashMap<String, Value>, timestamp: f32) {
46        let payload = EventPayload {
47            event_name,
48            data,
49            timestamp,
50        };
51        self.emitted_events.push(payload);
52    }
53
54    /// Get handlers for a specific event
55    pub fn get_handlers(&self, event_name: &str) -> Vec<EventHandler> {
56        self.handlers.get(event_name).cloned().unwrap_or_default()
57    }
58
59    /// Get all handlers matching a pattern (supports wildcards)
60    pub fn get_handlers_matching(&self, pattern: &str) -> Vec<EventHandler> {
61        let mut matching = Vec::new();
62
63        for (event_name, handlers) in &self.handlers {
64            if pattern_matches(pattern, event_name) {
65                matching.extend(handlers.clone());
66            }
67        }
68
69        matching
70    }
71
72    /// Check if a "once" handler should be executed
73    pub fn should_execute_once(&mut self, event_name: &str, handler_index: usize) -> bool {
74        let key = format!("{}:{}", event_name, handler_index);
75
76        if self.executed_once.contains_key(&key) {
77            return false;
78        }
79
80        self.executed_once.insert(key, 1);
81        true
82    }
83
84    /// Get all emitted events
85    pub fn get_emitted_events(&self) -> &[EventPayload] {
86        &self.emitted_events
87    }
88
89    /// Get emitted events for a specific event name
90    pub fn get_events_by_name(&self, event_name: &str) -> Vec<EventPayload> {
91        self.emitted_events
92            .iter()
93            .filter(|e| e.event_name == event_name)
94            .cloned()
95            .collect()
96    }
97
98    /// Clear all emitted events (useful for new playback runs)
99    pub fn clear_events(&mut self) {
100        self.emitted_events.clear();
101        self.executed_once.clear();
102    }
103
104    /// Get number of handlers for an event
105    pub fn handler_count(&self, event_name: &str) -> usize {
106        self.handlers.get(event_name).map(|h| h.len()).unwrap_or(0)
107    }
108
109    /// Remove all handlers for an event
110    pub fn remove_handlers(&mut self, event_name: &str) {
111        self.handlers.remove(event_name);
112    }
113}
114
115/// Check if a pattern matches an event name
116/// Supports wildcards: * (any characters), ? (single character)
117fn pattern_matches(pattern: &str, event_name: &str) -> bool {
118    if pattern == "*" {
119        return true;
120    }
121
122    if !pattern.contains('*') && !pattern.contains('?') {
123        return pattern == event_name;
124    }
125
126    // Convert pattern to regex-like matching
127    let mut pattern_chars = pattern.chars().peekable();
128    let mut name_chars = event_name.chars().peekable();
129
130    while pattern_chars.peek().is_some() || name_chars.peek().is_some() {
131        match pattern_chars.peek() {
132            Some('*') => {
133                pattern_chars.next();
134
135                // If * is at the end, match everything remaining
136                if pattern_chars.peek().is_none() {
137                    return true;
138                }
139
140                // Try to match remaining pattern with remaining name
141                let remaining_pattern: String = pattern_chars.clone().collect();
142                while name_chars.peek().is_some() {
143                    let remaining_name: String = name_chars.clone().collect();
144                    if pattern_matches(&remaining_pattern, &remaining_name) {
145                        return true;
146                    }
147                    name_chars.next();
148                }
149                return false;
150            }
151            Some('?') => {
152                pattern_chars.next();
153                if name_chars.next().is_none() {
154                    return false;
155                }
156            }
157            Some(p) => {
158                let p = *p;
159                pattern_chars.next();
160                match name_chars.next() {
161                    Some(n) if n == p => continue,
162                    _ => return false,
163                }
164            }
165            None => {
166                return name_chars.peek().is_none();
167            }
168        }
169    }
170
171    true
172}
173
174/// Built-in event types
175pub mod builtin_events {
176    pub const BEAT: &str = "beat";
177    pub const BAR: &str = "bar";
178    pub const START: &str = "start";
179    pub const END: &str = "end";
180    pub const TEMPO_CHANGE: &str = "tempo.change";
181    pub const NOTE_ON: &str = "note.on";
182    pub const NOTE_OFF: &str = "note.off";
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn test_register_and_get_handler() {
191        let mut registry = EventRegistry::new();
192
193        let handler = EventHandler {
194            event_name: "test".to_string(),
195            body: vec![],
196            once: false,
197        };
198
199        registry.register_handler(handler);
200
201        let handlers = registry.get_handlers("test");
202        assert_eq!(handlers.len(), 1);
203        assert_eq!(handlers[0].event_name, "test");
204    }
205
206    #[test]
207    fn test_emit_and_get_events() {
208        let mut registry = EventRegistry::new();
209
210        let mut data = HashMap::new();
211        data.insert("value".to_string(), Value::Number(42.0));
212
213        registry.emit("test".to_string(), data, 0.0);
214
215        let events = registry.get_events_by_name("test");
216        assert_eq!(events.len(), 1);
217        assert_eq!(events[0].event_name, "test");
218    }
219
220    #[test]
221    fn test_pattern_matches() {
222        assert!(pattern_matches("*", "anything"));
223        assert!(pattern_matches("test", "test"));
224        assert!(!pattern_matches("test", "other"));
225        assert!(pattern_matches("test*", "test123"));
226        assert!(pattern_matches("*test", "mytest"));
227        assert!(pattern_matches("te?t", "test"));
228        assert!(!pattern_matches("te?t", "teast"));
229    }
230
231    #[test]
232    fn test_once_handler() {
233        let mut registry = EventRegistry::new();
234
235        // First execution should return true
236        assert!(registry.should_execute_once("test", 0));
237
238        // Second execution should return false
239        assert!(!registry.should_execute_once("test", 0));
240    }
241
242    #[test]
243    fn test_wildcard_handlers() {
244        let mut registry = EventRegistry::new();
245
246        registry.register_handler(EventHandler {
247            event_name: "note.on".to_string(),
248            body: vec![],
249            once: false,
250        });
251
252        registry.register_handler(EventHandler {
253            event_name: "note.off".to_string(),
254            body: vec![],
255            once: false,
256        });
257
258        let handlers = registry.get_handlers_matching("note.*");
259        assert_eq!(handlers.len(), 2);
260    }
261
262    #[test]
263    fn test_clear_events() {
264        let mut registry = EventRegistry::new();
265
266        registry.emit("test".to_string(), HashMap::new(), 0.0);
267        assert_eq!(registry.get_emitted_events().len(), 1);
268
269        registry.clear_events();
270        assert_eq!(registry.get_emitted_events().len(), 0);
271    }
272}