use std::any::Any;
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, RwLock};
#[derive(Clone, Debug)]
pub struct HandlerRegistry {
handlers: Arc<RwLock<HashMap<String, HandlerEntry>>>,
}
#[derive(Clone)]
#[allow(clippy::type_complexity)]
pub enum HandlerEntry {
Simple(Arc<dyn Fn(&mut dyn Any) + Send + Sync>),
WithValue(Arc<dyn Fn(&mut dyn Any, Box<dyn Any>) + Send + Sync>),
WithCommand(Arc<dyn Fn(&mut dyn Any) -> Box<dyn Any> + Send + Sync>),
WithShared(Arc<dyn Fn(&mut dyn Any, &dyn Any) + Send + Sync>),
WithValueAndShared(Arc<dyn Fn(&mut dyn Any, Box<dyn Any>, &dyn Any) + Send + Sync>),
WithCommandAndShared(Arc<dyn Fn(&mut dyn Any, &dyn Any) -> Box<dyn Any> + Send + Sync>),
WithCanvasEvent(Arc<dyn Fn(&mut dyn Any, CanvasEvent) + Send + Sync>),
}
impl std::fmt::Debug for HandlerEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
HandlerEntry::Simple(_) => f.write_str("Simple(handler)"),
HandlerEntry::WithValue(_) => f.write_str("WithValue(handler)"),
HandlerEntry::WithCommand(_) => f.write_str("WithCommand(handler)"),
HandlerEntry::WithShared(_) => f.write_str("WithShared(handler)"),
HandlerEntry::WithValueAndShared(_) => f.write_str("WithValueAndShared(handler)"),
HandlerEntry::WithCommandAndShared(_) => f.write_str("WithCommandAndShared(handler)"),
HandlerEntry::WithCanvasEvent(_) => f.write_str("WithCanvasEvent(handler)"),
}
}
}
impl HandlerRegistry {
pub fn new() -> Self {
Self {
handlers: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn register_simple<F>(&self, name: &str, handler: F)
where
F: Fn(&mut dyn Any) + Send + Sync + 'static,
{
if let Ok(mut handlers) = self.handlers.write() {
handlers.insert(name.to_string(), HandlerEntry::Simple(Arc::new(handler)));
}
}
pub fn register_with_value<F>(&self, name: &str, handler: F)
where
F: Fn(&mut dyn Any, Box<dyn Any>) + Send + Sync + 'static,
{
if let Ok(mut handlers) = self.handlers.write() {
handlers.insert(name.to_string(), HandlerEntry::WithValue(Arc::new(handler)));
}
}
pub fn register_with_command<F>(&self, name: &str, handler: F)
where
F: Fn(&mut dyn Any) -> Box<dyn Any> + Send + Sync + 'static,
{
if let Ok(mut handlers) = self.handlers.write() {
handlers.insert(
name.to_string(),
HandlerEntry::WithCommand(Arc::new(handler)),
);
}
}
pub fn register_with_shared<F>(&self, name: &str, handler: F)
where
F: Fn(&mut dyn Any, &dyn Any) + Send + Sync + 'static,
{
if let Ok(mut handlers) = self.handlers.write() {
handlers.insert(
name.to_string(),
HandlerEntry::WithShared(Arc::new(handler)),
);
}
}
pub fn register_with_value_and_shared<F>(&self, name: &str, handler: F)
where
F: Fn(&mut dyn Any, Box<dyn Any>, &dyn Any) + Send + Sync + 'static,
{
if let Ok(mut handlers) = self.handlers.write() {
handlers.insert(
name.to_string(),
HandlerEntry::WithValueAndShared(Arc::new(handler)),
);
}
}
pub fn register_with_command_and_shared<F>(&self, name: &str, handler: F)
where
F: Fn(&mut dyn Any, &dyn Any) -> Box<dyn Any> + Send + Sync + 'static,
{
if let Ok(mut handlers) = self.handlers.write() {
handlers.insert(
name.to_string(),
HandlerEntry::WithCommandAndShared(Arc::new(handler)),
);
}
}
pub fn register_canvas_event<F>(&self, name: &str, handler: F)
where
F: Fn(&mut dyn Any, CanvasEvent) + Send + Sync + 'static,
{
if let Ok(mut handlers) = self.handlers.write() {
handlers.insert(
name.to_string(),
HandlerEntry::WithCanvasEvent(Arc::new(handler)),
);
}
}
pub fn get(&self, name: &str) -> Option<HandlerEntry> {
self.handlers.read().ok()?.get(name).cloned()
}
pub fn dispatch(&self, handler_name: &str, model: &mut dyn Any, value: Option<String>) {
if let Some(entry) = self.get(handler_name) {
match entry {
HandlerEntry::Simple(h) => h(model),
HandlerEntry::WithValue(h) => {
let val = value.unwrap_or_default();
h(model, Box::new(val));
}
HandlerEntry::WithCommand(h) => {
h(model);
}
HandlerEntry::WithShared(_)
| HandlerEntry::WithValueAndShared(_)
| HandlerEntry::WithCommandAndShared(_)
| HandlerEntry::WithCanvasEvent(_) => {
}
}
}
}
pub fn dispatch_with_command(
&self,
handler_name: &str,
model: &mut dyn Any,
value: Option<String>,
) -> Option<Box<dyn Any>> {
if let Some(entry) = self.get(handler_name) {
match entry {
HandlerEntry::Simple(h) => {
h(model);
None
}
HandlerEntry::WithValue(h) => {
let val = value.unwrap_or_default();
h(model, Box::new(val));
None
}
HandlerEntry::WithCommand(h) => Some(h(model)),
HandlerEntry::WithShared(_)
| HandlerEntry::WithValueAndShared(_)
| HandlerEntry::WithCommandAndShared(_)
| HandlerEntry::WithCanvasEvent(_) => None,
}
} else {
None
}
}
pub fn dispatch_with_shared(
&self,
handler_name: &str,
model: &mut dyn Any,
shared: &dyn Any,
value: Option<String>,
) -> Option<Box<dyn Any>> {
let entry = self.get(handler_name)?;
match entry {
HandlerEntry::Simple(h) => {
h(model);
None
}
HandlerEntry::WithValue(h) => {
h(model, Box::new(value.unwrap_or_default()));
None
}
HandlerEntry::WithCommand(h) => Some(h(model)),
HandlerEntry::WithShared(h) => {
h(model, shared);
None
}
HandlerEntry::WithValueAndShared(h) => {
h(model, Box::new(value.unwrap_or_default()), shared);
None
}
HandlerEntry::WithCommandAndShared(h) => Some(h(model, shared)),
HandlerEntry::WithCanvasEvent(_) => None,
}
}
pub fn dispatch_canvas_event(
&self,
handler_name: &str,
model: &mut dyn Any,
event: CanvasEvent,
) {
if let Some(HandlerEntry::WithCanvasEvent(h)) = self.get(handler_name) {
h(model, event);
}
}
pub fn contains(&self, name: &str) -> bool {
if let Ok(handlers) = self.handlers.read() {
handlers.contains_key(name)
} else {
false
}
}
}
impl Default for HandlerRegistry {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct HandlerSignature {
pub name: String,
pub param_type: Option<String>,
pub returns_command: bool,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct HandlerCallGraph {
dependencies: HashMap<String, Vec<String>>,
}
impl HandlerCallGraph {
pub fn new() -> Self {
Self {
dependencies: HashMap::new(),
}
}
pub fn add_dependency(&mut self, from: &str, to: &str) {
self.dependencies
.entry(from.to_string())
.or_default()
.push(to.to_string());
}
pub fn would_create_cycle(&self, from: &str, to: &str) -> bool {
let mut visited = HashSet::new();
self.can_reach(to, from, &mut visited)
}
fn can_reach(&self, from: &str, to: &str, visited: &mut HashSet<String>) -> bool {
if from == to {
return true;
}
if visited.contains(from) {
return false;
}
visited.insert(from.to_string());
if let Some(deps) = self.dependencies.get(from) {
for dep in deps {
if self.can_reach(dep, to, visited) {
return true;
}
}
}
false
}
pub fn dependents_of(&self, handler: &str) -> Vec<String> {
self.dependencies
.iter()
.filter_map(|(k, v)| {
if v.contains(&handler.to_string()) {
Some(k.clone())
} else {
None
}
})
.collect()
}
pub fn detect_cycles(&self) -> Option<Vec<String>> {
let mut visited = HashSet::new();
let mut recursion_stack = HashSet::new();
let mut path = Vec::new();
for handler in self.dependencies.keys() {
if !visited.contains(handler)
&& let Some(cycle) =
self.dfs_detect_cycle(handler, &mut visited, &mut recursion_stack, &mut path)
{
return Some(cycle);
}
}
None
}
fn dfs_detect_cycle(
&self,
handler: &str,
visited: &mut HashSet<String>,
recursion_stack: &mut HashSet<String>,
path: &mut Vec<String>,
) -> Option<Vec<String>> {
visited.insert(handler.to_string());
recursion_stack.insert(handler.to_string());
path.push(handler.to_string());
if let Some(deps) = self.dependencies.get(handler) {
for dep in deps {
if !visited.contains(dep) {
if let Some(cycle) = self.dfs_detect_cycle(dep, visited, recursion_stack, path)
{
return Some(cycle);
}
} else if recursion_stack.contains(dep) {
if let Some(cycle_start) = path.iter().position(|h| h == dep) {
let mut cycle = path[cycle_start..].to_vec();
cycle.push(dep.to_string());
return Some(cycle);
}
}
}
}
path.pop();
recursion_stack.remove(handler);
None
}
}
impl Default for HandlerCallGraph {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct CanvasEvent {
pub kind: CanvasEventKind,
pub x: f32,
pub y: f32,
pub delta_x: Option<f32>,
pub delta_y: Option<f32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum CanvasEventKind {
Click,
Drag,
Move,
Release,
}