dampen_core/handler/
mod.rs

1//! Handler system for event dispatch
2
3use std::any::Any;
4use std::collections::{HashMap, HashSet};
5use std::sync::{Arc, RwLock};
6
7/// Registry of event handlers
8#[derive(Clone, Debug)]
9pub struct HandlerRegistry {
10    handlers: Arc<RwLock<HashMap<String, HandlerEntry>>>,
11}
12
13/// Entry in the handler registry
14#[derive(Clone)]
15#[allow(clippy::type_complexity)]
16pub enum HandlerEntry {
17    /// Simple handler: `fn(&mut Model)`
18    Simple(Arc<dyn Fn(&mut dyn Any) + Send + Sync>),
19
20    /// Handler with value: `fn(&mut Model, T)`
21    WithValue(Arc<dyn Fn(&mut dyn Any, Box<dyn Any>) + Send + Sync>),
22
23    /// Handler returning command: `fn(&mut Model) -> Command<Message>`
24    WithCommand(Arc<dyn Fn(&mut dyn Any) -> Box<dyn Any> + Send + Sync>),
25}
26
27impl std::fmt::Debug for HandlerEntry {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        match self {
30            HandlerEntry::Simple(_) => f.write_str("Simple(handler)"),
31            HandlerEntry::WithValue(_) => f.write_str("WithValue(handler)"),
32            HandlerEntry::WithCommand(_) => f.write_str("WithCommand(handler)"),
33        }
34    }
35}
36
37impl HandlerRegistry {
38    /// Create a new empty handler registry
39    pub fn new() -> Self {
40        Self {
41            handlers: Arc::new(RwLock::new(HashMap::new())),
42        }
43    }
44
45    /// Register a simple handler
46    pub fn register_simple<F>(&self, name: &str, handler: F)
47    where
48        F: Fn(&mut dyn Any) + Send + Sync + 'static,
49    {
50        if let Ok(mut handlers) = self.handlers.write() {
51            handlers.insert(name.to_string(), HandlerEntry::Simple(Arc::new(handler)));
52        }
53    }
54
55    /// Register a handler with a value parameter
56    pub fn register_with_value<F>(&self, name: &str, handler: F)
57    where
58        F: Fn(&mut dyn Any, Box<dyn Any>) + Send + Sync + 'static,
59    {
60        if let Ok(mut handlers) = self.handlers.write() {
61            handlers.insert(name.to_string(), HandlerEntry::WithValue(Arc::new(handler)));
62        }
63    }
64
65    /// Register a handler that returns a command
66    pub fn register_with_command<F>(&self, name: &str, handler: F)
67    where
68        F: Fn(&mut dyn Any) -> Box<dyn Any> + Send + Sync + 'static,
69    {
70        if let Ok(mut handlers) = self.handlers.write() {
71            handlers.insert(
72                name.to_string(),
73                HandlerEntry::WithCommand(Arc::new(handler)),
74            );
75        }
76    }
77
78    /// Look up a handler by name
79    pub fn get(&self, name: &str) -> Option<HandlerEntry> {
80        self.handlers.read().ok()?.get(name).cloned()
81    }
82
83    /// Dispatches a handler by name, executing it with the provided model and optional value.
84    ///
85    /// This is a convenience method that combines `get()` and handler invocation.
86    ///
87    /// # Arguments
88    ///
89    /// * `handler_name` - Name of the handler to dispatch
90    /// * `model` - Mutable reference to the model (as `&mut dyn Any`)
91    /// * `value` - Optional string value passed to WithValue handlers
92    ///
93    /// # Example
94    ///
95    /// ```rust,ignore
96    /// use dampen_core::HandlerRegistry;
97    ///
98    /// let registry = HandlerRegistry::new();
99    /// registry.register_simple("greet", |model| {
100    ///     let model = model.downcast_mut::<MyModel>().unwrap();
101    ///     model.count += 1;
102    /// });
103    ///
104    /// let model = &mut MyModel { count: 0 } as &mut dyn std::any::Any;
105    /// registry.dispatch("greet", model, None);
106    /// ```
107    pub fn dispatch(&self, handler_name: &str, model: &mut dyn Any, value: Option<String>) {
108        if let Some(entry) = self.get(handler_name) {
109            match entry {
110                HandlerEntry::Simple(h) => h(model),
111                HandlerEntry::WithValue(h) => {
112                    let val = value.unwrap_or_default();
113                    h(model, Box::new(val));
114                }
115                HandlerEntry::WithCommand(h) => {
116                    h(model);
117                }
118            }
119        }
120    }
121
122    /// Check if a handler exists
123    pub fn contains(&self, name: &str) -> bool {
124        if let Ok(handlers) = self.handlers.read() {
125            handlers.contains_key(name)
126        } else {
127            false
128        }
129    }
130}
131
132impl Default for HandlerRegistry {
133    fn default() -> Self {
134        Self::new()
135    }
136}
137
138/// Handler metadata for compile-time validation
139#[derive(Debug, Clone, PartialEq)]
140pub struct HandlerSignature {
141    /// Handler name
142    pub name: String,
143
144    /// Parameter type if applicable
145    pub param_type: Option<String>,
146
147    /// Whether handler returns Command
148    pub returns_command: bool,
149}
150
151/// Build-time analysis structure for circular dependency detection
152#[derive(Debug, Clone)]
153pub struct HandlerCallGraph {
154    /// Map of handler name to its dependencies
155    dependencies: HashMap<String, Vec<String>>,
156}
157
158impl HandlerCallGraph {
159    /// Create a new empty call graph
160    pub fn new() -> Self {
161        Self {
162            dependencies: HashMap::new(),
163        }
164    }
165
166    /// Add a dependency edge (from depends on to)
167    pub fn add_dependency(&mut self, from: &str, to: &str) {
168        self.dependencies
169            .entry(from.to_string())
170            .or_default()
171            .push(to.to_string());
172    }
173
174    /// Detect if adding edge would create a cycle
175    pub fn would_create_cycle(&self, from: &str, to: &str) -> bool {
176        // Check if 'to' can reach 'from' (which would create a cycle)
177        let mut visited = HashSet::new();
178        self.can_reach(to, from, &mut visited)
179    }
180
181    /// Check if 'from' can reach 'to' via dependencies
182    fn can_reach(&self, from: &str, to: &str, visited: &mut HashSet<String>) -> bool {
183        if from == to {
184            return true;
185        }
186
187        if visited.contains(from) {
188            return false;
189        }
190
191        visited.insert(from.to_string());
192
193        if let Some(deps) = self.dependencies.get(from) {
194            for dep in deps {
195                if self.can_reach(dep, to, visited) {
196                    return true;
197                }
198            }
199        }
200
201        false
202    }
203
204    /// Get all handlers that depend on the given handler
205    pub fn dependents_of(&self, handler: &str) -> Vec<String> {
206        self.dependencies
207            .iter()
208            .filter_map(|(k, v)| {
209                if v.contains(&handler.to_string()) {
210                    Some(k.clone())
211                } else {
212                    None
213                }
214            })
215            .collect()
216    }
217
218    /// Detect cycles in the call graph using DFS
219    pub fn detect_cycles(&self) -> Option<Vec<String>> {
220        let mut visited = HashSet::new();
221        let mut recursion_stack = HashSet::new();
222        let mut path = Vec::new();
223
224        for handler in self.dependencies.keys() {
225            if !visited.contains(handler) {
226                if let Some(cycle) =
227                    self.dfs_detect_cycle(handler, &mut visited, &mut recursion_stack, &mut path)
228                {
229                    return Some(cycle);
230                }
231            }
232        }
233
234        None
235    }
236
237    fn dfs_detect_cycle(
238        &self,
239        handler: &str,
240        visited: &mut HashSet<String>,
241        recursion_stack: &mut HashSet<String>,
242        path: &mut Vec<String>,
243    ) -> Option<Vec<String>> {
244        visited.insert(handler.to_string());
245        recursion_stack.insert(handler.to_string());
246        path.push(handler.to_string());
247
248        if let Some(deps) = self.dependencies.get(handler) {
249            for dep in deps {
250                if !visited.contains(dep) {
251                    if let Some(cycle) = self.dfs_detect_cycle(dep, visited, recursion_stack, path)
252                    {
253                        return Some(cycle);
254                    }
255                } else if recursion_stack.contains(dep) {
256                    // Found a cycle - construct the cycle path
257                    if let Some(cycle_start) = path.iter().position(|h| h == dep) {
258                        let mut cycle = path[cycle_start..].to_vec();
259                        cycle.push(dep.to_string());
260                        return Some(cycle);
261                    }
262                }
263            }
264        }
265
266        path.pop();
267        recursion_stack.remove(handler);
268        None
269    }
270}
271
272impl Default for HandlerCallGraph {
273    fn default() -> Self {
274        Self::new()
275    }
276}