use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, RwLock};
pub type EngineRouteCallback = Arc<
dyn Fn(
serde_json::Value,
) -> Pin<Box<dyn Future<Output = anyhow::Result<serde_json::Value>> + Send>>
+ Send
+ Sync,
>;
#[derive(Clone, Default)]
pub struct EngineRouteRegistry {
routes: Arc<RwLock<HashMap<String, EngineRouteCallback>>>,
}
impl EngineRouteRegistry {
pub fn new() -> Self {
Self {
routes: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn register(&self, route: &str, callback: EngineRouteCallback) {
let mut routes = self.routes.write().unwrap();
routes.insert(route.to_string(), callback);
tracing::debug!("Registered engine route: /engine/{}", route);
}
pub fn get(&self, route: &str) -> Option<EngineRouteCallback> {
let routes = self.routes.read().unwrap();
routes.get(route).cloned()
}
pub fn routes(&self) -> Vec<String> {
let routes = self.routes.read().unwrap();
routes.keys().cloned().collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_registry_basic() {
let registry = EngineRouteRegistry::new();
let callback: EngineRouteCallback =
Arc::new(|body| Box::pin(async move { Ok(serde_json::json!({"echo": body})) }));
registry.register("test", callback);
assert!(registry.get("test").is_some());
assert!(registry.get("nonexistent").is_none());
let routes = registry.routes();
assert_eq!(routes.len(), 1);
assert!(routes.contains(&"test".to_string()));
}
#[tokio::test]
async fn test_callback_execution() {
let registry = EngineRouteRegistry::new();
let callback: EngineRouteCallback = Arc::new(|body| {
Box::pin(async move {
let input = body.get("input").and_then(|v| v.as_str()).unwrap_or("");
Ok(serde_json::json!({
"output": format!("processed: {}", input)
}))
})
});
registry.register("process", callback);
let cb = registry.get("process").unwrap();
let result = cb(serde_json::json!({"input": "test"})).await.unwrap();
assert_eq!(result["output"], "processed: test");
}
#[tokio::test]
async fn test_clone_shares_routes() {
let registry = EngineRouteRegistry::new();
let callback: EngineRouteCallback =
Arc::new(|_| Box::pin(async { Ok(serde_json::json!({"ok": true})) }));
registry.register("test", callback);
let cloned = registry.clone();
assert!(registry.get("test").is_some());
assert!(cloned.get("test").is_some());
let callback2: EngineRouteCallback =
Arc::new(|_| Box::pin(async { Ok(serde_json::json!({"ok": false})) }));
cloned.register("test2", callback2);
assert!(registry.get("test2").is_some());
}
}