use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::{broadcast, mpsc};
use crate::integration::{CoderEvent, HostEvent, HostCommand};
use crate::core::error::IntegrationError;
pub struct EventBus {
coder_event_tx: broadcast::Sender<CoderEvent>,
host_event_tx: broadcast::Sender<HostEvent>,
host_command_tx: mpsc::Sender<HostCommand>,
handlers: HashMap<String, Box<dyn EventHandler>>,
}
pub trait EventHandler: Send + Sync {
fn handle_coder_event(&self, event: &CoderEvent) -> Result<Option<HostCommand>, IntegrationError>;
fn handle_host_event(&self, event: &HostEvent) -> Result<Option<CoderEvent>, IntegrationError>;
}
impl EventBus {
pub fn new() -> Self {
let (coder_event_tx, _) = broadcast::channel(1000);
let (host_event_tx, _) = broadcast::channel(1000);
let (host_command_tx, _) = mpsc::channel(100);
Self {
coder_event_tx,
host_event_tx,
host_command_tx,
handlers: HashMap::new(),
}
}
pub fn register_handler(&mut self, name: String, handler: Box<dyn EventHandler>) {
self.handlers.insert(name, handler);
}
pub fn send_coder_event(&self, event: CoderEvent) -> Result<(), IntegrationError> {
self.coder_event_tx.send(event)
.map_err(|e| IntegrationError::Communication(e.to_string()))?;
Ok(())
}
pub fn send_host_event(&self, event: HostEvent) -> Result<(), IntegrationError> {
self.host_event_tx.send(event)
.map_err(|e| IntegrationError::Communication(e.to_string()))?;
Ok(())
}
pub async fn send_host_command(&self, command: HostCommand) -> Result<(), IntegrationError> {
self.host_command_tx.send(command).await
.map_err(|e| IntegrationError::Communication(e.to_string()))?;
Ok(())
}
pub fn subscribe_coder_events(&self) -> broadcast::Receiver<CoderEvent> {
self.coder_event_tx.subscribe()
}
pub fn subscribe_host_events(&self) -> broadcast::Receiver<HostEvent> {
self.host_event_tx.subscribe()
}
pub fn get_host_command_receiver(&mut self) -> mpsc::Receiver<HostCommand> {
let (tx, rx) = mpsc::channel(100);
self.host_command_tx = tx;
rx
}
pub async fn run(&self) -> Result<(), IntegrationError> {
let mut coder_rx = self.subscribe_coder_events();
let mut host_rx = self.subscribe_host_events();
loop {
tokio::select! {
Ok(coder_event) = coder_rx.recv() => {
self.process_coder_event(&coder_event).await?;
}
Ok(host_event) = host_rx.recv() => {
self.process_host_event(&host_event).await?;
}
else => break,
}
}
Ok(())
}
async fn process_coder_event(&self, event: &CoderEvent) -> Result<(), IntegrationError> {
for handler in self.handlers.values() {
if let Some(command) = handler.handle_coder_event(event)? {
self.send_host_command(command).await?;
}
}
Ok(())
}
async fn process_host_event(&self, event: &HostEvent) -> Result<(), IntegrationError> {
for handler in self.handlers.values() {
if let Some(coder_event) = handler.handle_host_event(event)? {
self.send_coder_event(coder_event)?;
}
}
Ok(())
}
}
impl Default for EventBus {
fn default() -> Self {
Self::new()
}
}
pub struct EventBridge {
event_bus: Arc<EventBus>,
}
impl EventBridge {
pub fn new(event_bus: Arc<EventBus>) -> Self {
Self { event_bus }
}
pub async fn forward_coder_events(&self) -> Result<(), IntegrationError> {
let mut rx = self.event_bus.subscribe_coder_events();
while let Ok(event) = rx.recv().await {
tracing::debug!("Forwarding CoderLib event: {:?}", event);
}
Ok(())
}
pub async fn forward_host_events(&self) -> Result<(), IntegrationError> {
let mut rx = self.event_bus.subscribe_host_events();
while let Ok(event) = rx.recv().await {
tracing::debug!("Forwarding host event: {:?}", event);
}
Ok(())
}
}
pub struct DefaultEventHandler;
impl EventHandler for DefaultEventHandler {
fn handle_coder_event(&self, event: &CoderEvent) -> Result<Option<HostCommand>, IntegrationError> {
match event {
CoderEvent::ProcessingStarted { session_id } => {
Ok(Some(HostCommand::ShowMessage {
message: format!("AI processing started for session {}", session_id),
level: crate::integration::MessageLevel::Info,
}))
}
CoderEvent::ProcessingCompleted { session_id, response } => {
Ok(Some(HostCommand::ShowMessage {
message: format!("AI processing completed for session {}: {}", session_id, response),
level: crate::integration::MessageLevel::Success,
}))
}
CoderEvent::ProcessingFailed { session_id, error } => {
Ok(Some(HostCommand::ShowMessage {
message: format!("AI processing failed for session {}: {}", session_id, error),
level: crate::integration::MessageLevel::Error,
}))
}
CoderEvent::StatusUpdate { message } => {
Ok(Some(HostCommand::ShowMessage {
message: message.clone(),
level: crate::integration::MessageLevel::Info,
}))
}
_ => Ok(None),
}
}
fn handle_host_event(&self, event: &HostEvent) -> Result<Option<CoderEvent>, IntegrationError> {
match event {
HostEvent::FileOpened(path) => {
Ok(Some(CoderEvent::StatusUpdate {
message: format!("File opened: {}", path.display()),
}))
}
HostEvent::FileSaved(path) => {
Ok(Some(CoderEvent::StatusUpdate {
message: format!("File saved: {}", path.display()),
}))
}
HostEvent::ProjectOpened(path) => {
Ok(Some(CoderEvent::StatusUpdate {
message: format!("Project opened: {}", path.display()),
}))
}
_ => Ok(None),
}
}
}
pub struct EventFilter<F> {
filter_fn: F,
}
impl<F> EventFilter<F>
where
F: Fn(&CoderEvent) -> bool + Send + Sync,
{
pub fn new(filter_fn: F) -> Self {
Self { filter_fn }
}
}
impl<F> EventHandler for EventFilter<F>
where
F: Fn(&CoderEvent) -> bool + Send + Sync,
{
fn handle_coder_event(&self, event: &CoderEvent) -> Result<Option<HostCommand>, IntegrationError> {
if (self.filter_fn)(event) {
Ok(None)
} else {
Ok(None)
}
}
fn handle_host_event(&self, _event: &HostEvent) -> Result<Option<CoderEvent>, IntegrationError> {
Ok(None)
}
}
pub struct EventLogger {
log_coder_events: bool,
log_host_events: bool,
}
impl EventLogger {
pub fn new(log_coder_events: bool, log_host_events: bool) -> Self {
Self {
log_coder_events,
log_host_events,
}
}
}
impl EventHandler for EventLogger {
fn handle_coder_event(&self, event: &CoderEvent) -> Result<Option<HostCommand>, IntegrationError> {
if self.log_coder_events {
tracing::info!("CoderLib event: {:?}", event);
}
Ok(None)
}
fn handle_host_event(&self, event: &HostEvent) -> Result<Option<CoderEvent>, IntegrationError> {
if self.log_host_events {
tracing::info!("Host event: {:?}", event);
}
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[tokio::test]
async fn test_event_bus_creation() {
let event_bus = EventBus::new();
assert_eq!(event_bus.handlers.len(), 0);
}
#[tokio::test]
async fn test_event_sending() {
let event_bus = EventBus::new();
let _coder_rx = event_bus.subscribe_coder_events();
let _host_rx = event_bus.subscribe_host_events();
let coder_event = CoderEvent::ProcessingStarted {
session_id: "test".to_string(),
};
let result = event_bus.send_coder_event(coder_event);
assert!(result.is_ok());
let host_event = HostEvent::FileOpened(PathBuf::from("test.txt"));
let result = event_bus.send_host_event(host_event);
assert!(result.is_ok());
}
#[test]
fn test_default_event_handler() {
let handler = DefaultEventHandler;
let event = CoderEvent::ProcessingStarted {
session_id: "test".to_string(),
};
let result = handler.handle_coder_event(&event);
assert!(result.is_ok());
assert!(result.unwrap().is_some());
}
#[test]
fn test_event_filter() {
let filter = EventFilter::new(|event| {
matches!(event, CoderEvent::ProcessingStarted { .. })
});
let start_event = CoderEvent::ProcessingStarted {
session_id: "test".to_string(),
};
let complete_event = CoderEvent::ProcessingCompleted {
session_id: "test".to_string(),
response: "done".to_string(),
};
let result1 = filter.handle_coder_event(&start_event);
assert!(result1.is_ok());
let result2 = filter.handle_coder_event(&complete_event);
assert!(result2.is_ok());
}
}