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}