tcrm_task/tasks/event.rs
1use crate::tasks::{config::StreamSource, error::TaskError};
2
3/// Events emitted during task execution lifecycle
4///
5/// `TaskEvent` represents all events that occur during task execution,
6/// from process start to completion. These events enable real-time monitoring
7/// and event-driven 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, with exit code and reason
16/// 5. `Error` - Error related to task 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 /// The process has exited and all resources have been cleaned up.
131 Stopped {
132 /// Name of the task that stopped
133 task_name: String,
134 /// Exit code from the process (None if terminated)
135 exit_code: Option<i32>,
136 /// Reason the process stopped
137 reason: TaskEventStopReason,
138 },
139
140 /// An error occurred before task execution
141 ///
142 /// Emitted when errors occur during configuration validation,
143 /// process spawning, and will not emit any further events.
144 Error {
145 /// Name of the task that encountered an error
146 task_name: String,
147 /// The specific error that occurred
148 error: TaskError,
149 },
150}
151
152/// Reason why a task stopped executing
153///
154/// Provides detailed information about why a process completed,
155/// whether due to natural completion, termination, or error.
156///
157/// # Examples
158///
159/// ```rust
160/// use tcrm_task::tasks::{event::TaskEventStopReason, event::TaskTerminateReason};
161///
162/// // Natural completion
163/// let reason = TaskEventStopReason::Finished;
164///
165/// // Terminated due to timeout
166/// let reason = TaskEventStopReason::Terminated(TaskTerminateReason::Timeout);
167///
168/// // Terminated due to error
169/// let reason = TaskEventStopReason::Error("Process crashed".to_string());
170/// ```
171#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
172#[derive(Debug, Clone, PartialEq)]
173pub enum TaskEventStopReason {
174 /// Process completed normally with an exit code
175 Finished,
176
177 /// Process was terminated for a specific reason
178 Terminated(TaskTerminateReason),
179
180 /// Process stopped due to an error
181 Error(String),
182}
183
184/// Reason for terminating a running task
185///
186/// Provides context about why a task termination was requested,
187/// enabling appropriate cleanup and response handling.
188///
189/// # Examples
190///
191/// ## Timeout Termination
192/// ```rust
193/// use tcrm_task::tasks::{
194/// config::TaskConfig,
195/// async_tokio::spawner::TaskSpawner,
196/// event::TaskTerminateReason
197/// };
198/// use tokio::sync::mpsc;
199///
200/// #[tokio::main]
201/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
202/// let config = TaskConfig::new("cmd").args(["/C", "ping", "127.0.0.1", "-n", "5"]); // 5 second sleep
203/// let mut spawner = TaskSpawner::new("long-task".to_string(), config);
204///
205/// let (tx, _rx) = mpsc::channel(100);
206/// spawner.start_direct(tx).await?;
207///
208/// // Terminate after 1 second
209/// tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
210/// spawner.send_terminate_signal(TaskTerminateReason::Timeout).await?;
211///
212/// Ok(())
213/// }
214/// ```
215///
216/// ## Cleanup Termination
217/// ```rust
218/// use tcrm_task::tasks::{
219/// config::TaskConfig,
220/// async_tokio::spawner::TaskSpawner,
221/// event::TaskTerminateReason
222/// };
223/// use tokio::sync::mpsc;
224///
225/// #[tokio::main]
226/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
227/// let config = TaskConfig::new("cmd").args(["/C", "echo", "running"]);
228/// let mut spawner = TaskSpawner::new("daemon".to_string(), config);
229///
230/// let (tx, _rx) = mpsc::channel(100);
231/// spawner.start_direct(tx).await?;
232///
233/// // Cleanup shutdown reason
234/// let reason = TaskTerminateReason::Cleanup;
235/// spawner.send_terminate_signal(reason).await?;
236///
237/// Ok(())
238/// }
239/// ```
240#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
241#[derive(Debug, Clone, PartialEq)]
242pub enum TaskTerminateReason {
243 /// Task exceeded its configured timeout
244 ///
245 /// The process ran longer than the `timeout_ms` specified in `TaskConfig`
246 /// and was terminated to prevent runaway processes.
247 Timeout,
248
249 /// Task was terminated during cleanup operations
250 ///
251 /// Used when terminating tasks as part of application shutdown,
252 /// resource cleanup, or dependency management.
253 Cleanup,
254
255 /// Task was terminated because its dependencies finished
256 ///
257 /// Used in task orchestration scenarios where tasks depend on
258 /// other tasks and should be terminated when dependencies complete.
259 DependenciesFinished,
260
261 /// Task was terminated by explicit user request
262 ///
263 /// Used when user or external library requests the task to stop.
264 UserRequested,
265}