tcrm_task/tasks/event.rs
1use crate::tasks::{config::StreamSource, error::TaskError, state::TaskTerminateReason};
2
3/// Events emitted during task execution lifecycle
4///
5/// `TaskEvent` represents all significant events that occur during task execution,
6/// from process start to completion. These events enable real-time monitoring
7/// and reactive programming patterns.
8///
9/// # Event Flow
10///
11/// A typical task execution emits events in this order:
12/// 1. `Started` - Process has been spawned
13/// 2. `Output` - Output lines from stdout/stderr (ongoing)
14/// 3. `Ready` - Ready indicator detected (optional, for long-running processes)
15/// 4. `Stopped` - Process has completed
16/// 5. `Error` - If any error occurs during execution
17///
18/// # Examples
19///
20/// ## Basic Event Processing
21/// ```rust
22/// use tcrm_task::tasks::{config::TaskConfig, async_tokio::spawner::TaskSpawner, event::TaskEvent};
23/// use tokio::sync::mpsc;
24///
25/// #[tokio::main]
26/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
27/// let config = TaskConfig::new("cmd").args(["/C", "echo", "hello", "world"]);
28/// let mut spawner = TaskSpawner::new("demo".to_string(), config);
29///
30/// let (tx, mut rx) = mpsc::channel(100);
31/// spawner.start_direct(tx).await?;
32///
33/// while let Some(event) = rx.recv().await {
34/// match event {
35/// TaskEvent::Started { task_name } => {
36/// println!("Task '{}' started", task_name);
37/// }
38/// TaskEvent::Output { task_name, line, src } => {
39/// println!("Task '{}' output: {}", task_name, line);
40/// }
41/// TaskEvent::Stopped { task_name, exit_code, reason } => {
42/// println!("Task '{}' stopped with code {:?}", task_name, exit_code);
43/// break;
44/// }
45/// TaskEvent::Error { task_name, error } => {
46/// eprintln!("Task '{}' error: {}", task_name, error);
47/// break;
48/// }
49/// _ => {}
50/// }
51/// }
52///
53/// Ok(())
54/// }
55/// ```
56///
57/// ## Server Ready Detection
58/// ```rust
59/// use tcrm_task::tasks::{
60/// config::{TaskConfig, StreamSource},
61/// async_tokio::spawner::TaskSpawner,
62/// event::TaskEvent
63/// };
64/// use tokio::sync::mpsc;
65///
66/// #[tokio::main]
67/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
68/// let config = TaskConfig::new("cmd")
69/// .args(["/C", "echo", "Server listening"])
70/// .ready_indicator("Server listening")
71/// .ready_indicator_source(StreamSource::Stdout);
72///
73/// let mut spawner = TaskSpawner::new("server".to_string(), config);
74/// let (tx, mut rx) = mpsc::channel(100);
75/// spawner.start_direct(tx).await?;
76///
77/// while let Some(event) = rx.recv().await {
78/// match event {
79/// TaskEvent::Ready { task_name } => {
80/// println!("Server '{}' is ready for requests!", task_name);
81/// // Server is now ready - can start sending requests
82/// break;
83/// }
84/// TaskEvent::Output { line, .. } => {
85/// println!("Server log: {}", line);
86/// }
87/// _ => {}
88/// }
89/// }
90///
91/// Ok(())
92/// }
93/// ```
94#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
95#[derive(Debug, Clone, PartialEq)]
96pub enum TaskEvent {
97 /// Process has been successfully spawned and is running
98 ///
99 /// This is the first event emitted after successful process creation.
100 /// The process is now running and other events will follow.
101 Started {
102 /// Name of the task that started
103 task_name: String,
104 },
105
106 /// Output line received from the process
107 ///
108 /// Emitted for each line of output from stdout or stderr.
109 /// Lines are buffered and emitted when complete (on newline).
110 Output {
111 /// Name of the task that produced the output
112 task_name: String,
113 /// The output line (without trailing newline)
114 line: String,
115 /// Source stream (stdout or stderr)
116 src: StreamSource,
117 },
118
119 /// Process has signaled it's ready to accept requests
120 ///
121 /// Only emitted for long-running processes that have a ready indicator configured.
122 /// Indicates the process has completed initialization and is ready for work.
123 Ready {
124 /// Name of the task that became ready
125 task_name: String,
126 },
127
128 /// Process has completed execution
129 ///
130 /// This is the final event for successful task execution.
131 /// The process has exited and all resources have been cleaned up.
132 Stopped {
133 /// Name of the task that stopped
134 task_name: String,
135 /// Exit code from the process (None if terminated)
136 exit_code: Option<i32>,
137 /// Reason the process stopped
138 reason: TaskEventStopReason,
139 },
140
141 /// An error occurred during task execution
142 ///
143 /// Emitted when errors occur during configuration validation,
144 /// process spawning, or execution monitoring.
145 Error {
146 /// Name of the task that encountered an error
147 task_name: String,
148 /// The specific error that occurred
149 error: TaskError,
150 },
151}
152
153/// Reason why a task stopped executing
154///
155/// Provides detailed information about why a process completed,
156/// whether due to natural completion, termination, or error.
157///
158/// # Examples
159///
160/// ```rust
161/// use tcrm_task::tasks::{event::TaskEventStopReason, state::TaskTerminateReason};
162///
163/// // Natural completion
164/// let reason = TaskEventStopReason::Finished;
165///
166/// // Terminated due to timeout
167/// let reason = TaskEventStopReason::Terminated(TaskTerminateReason::Timeout);
168///
169/// // Terminated due to error
170/// let reason = TaskEventStopReason::Error("Process crashed".to_string());
171/// ```
172#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
173#[derive(Debug, Clone, PartialEq)]
174pub enum TaskEventStopReason {
175 /// Process completed normally with an exit code
176 Finished,
177
178 /// Process was terminated for a specific reason
179 Terminated(TaskTerminateReason),
180
181 /// Process stopped due to an error
182 Error(String),
183}