use super::core::{StepStatus, TaskStatus};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::broadcast;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskLifecycleEvent {
pub task_id: String,
pub event_type: TaskEventType,
pub data: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum TaskEventType {
Created,
StatusChanged { status: TaskStatus },
StepChanged { step_index: usize, step_name: String, status: StepStatus, output: Option<String>, error: Option<String>, duration_ms: Option<u64> },
Progress { progress: u8, message: String },
}
impl TaskLifecycleEvent {
pub fn created(task_id: String) -> Self {
Self { task_id, event_type: TaskEventType::Created, data: serde_json::Value::Null }
}
pub fn status_changed(task_id: String, status: TaskStatus) -> Self {
Self { task_id, event_type: TaskEventType::StatusChanged { status }, data: serde_json::Value::Null }
}
pub fn step_changed(
task_id: String,
step_index: usize,
step_name: String,
status: StepStatus,
output: Option<String>,
error: Option<String>,
duration_ms: Option<u64>,
) -> Self {
Self {
task_id,
event_type: TaskEventType::StepChanged { step_index, step_name, status, output, error, duration_ms },
data: serde_json::Value::Null,
}
}
pub fn progress(task_id: String, progress: u8, message: String) -> Self {
Self { task_id, event_type: TaskEventType::Progress { progress, message }, data: serde_json::Value::Null }
}
pub fn is_terminal(&self) -> bool {
match &self.event_type {
TaskEventType::StatusChanged { status } => {
matches!(status, TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled | TaskStatus::Timeout)
}
_ => false,
}
}
}
pub static TASK_POOL_EVENT_BUS: Lazy<Arc<broadcast::Sender<TaskLifecycleEvent>>> = Lazy::new(|| {
let (tx, _) = broadcast::channel(256);
Arc::new(tx)
});
pub struct TaskEventSubscriber {
receiver: broadcast::Receiver<TaskLifecycleEvent>,
}
impl TaskEventSubscriber {
pub fn new() -> Self {
Self { receiver: TASK_POOL_EVENT_BUS.subscribe() }
}
pub async fn recv(&mut self) -> Option<TaskLifecycleEvent> {
loop {
match self.receiver.recv().await {
Ok(event) => return Some(event),
Err(broadcast::error::RecvError::Closed) => return None,
Err(broadcast::error::RecvError::Lagged(n)) => {
eprintln!("[EventBus] Lagged, skipped {} events", n);
continue;
}
}
}
}
pub async fn recv_for_task(&mut self, target_task_id: &str) -> Option<TaskLifecycleEvent> {
while let Some(event) = self.recv().await {
if event.task_id == target_task_id {
return Some(event);
}
}
None
}
pub async fn wait_for_terminal(&mut self, target_task_id: &str) -> Option<TaskLifecycleEvent> {
while let Some(event) = self.recv().await {
if event.task_id == target_task_id && event.is_terminal() {
return Some(event);
}
}
None
}
pub async fn wait_for_status(&mut self, target_task_id: &str, target_status: TaskStatus) -> Option<TaskLifecycleEvent> {
while let Some(event) = self.recv().await {
if event.task_id != target_task_id {
continue;
}
if let TaskEventType::StatusChanged { status } = &event.event_type {
if *status == target_status {
return Some(event);
}
}
}
None
}
}
impl Default for TaskEventSubscriber {
fn default() -> Self {
Self::new()
}
}
pub fn publish_task_pool_event(event: TaskLifecycleEvent) {
let _ = TASK_POOL_EVENT_BUS.send(event);
}
pub fn subscribe() -> TaskEventSubscriber {
TaskEventSubscriber::new()
}