tcrm_monitor/monitor/
error.rs

1//! Error types for task monitoring operations.
2//!
3//! This module defines the error types that can occur during task monitoring,
4//! including dependency validation errors and stdin communication errors.
5
6use thiserror::Error;
7
8/// Main error type for task monitoring operations.
9///
10/// Covers various error conditions that can occur during task monitoring setup and execution,
11/// including configuration parsing, dependency validation, and runtime communication errors.
12///
13/// # Examples
14///
15/// ## Handling Circular Dependencies
16///
17/// ```rust,should_panic
18/// use std::collections::HashMap;
19/// use tcrm_monitor::monitor::{TaskMonitor, config::TaskSpec, error::TaskMonitorError};
20/// use tcrm_task::tasks::config::TaskConfig;
21///
22/// let mut tasks = HashMap::new();
23/// tasks.insert(
24///     "a".to_string(),
25///     TaskSpec::new(TaskConfig::new("echo").args(["a"]))
26///         .dependencies(["b"])
27/// );
28/// tasks.insert(
29///     "b".to_string(),
30///     TaskSpec::new(TaskConfig::new("echo").args(["b"]))
31///         .dependencies(["a"])
32/// );
33///
34/// match TaskMonitor::new(tasks) {
35///     Err(TaskMonitorError::CircularDependency(task)) => {
36///         println!("Circular dependency involving: {}", task);
37///         panic!("Expected circular dependency error");
38///     }
39///     _ => panic!("Expected error"),
40/// }
41/// ```
42///
43/// ## Handling Missing Dependencies
44///
45/// ```rust,should_panic
46/// use std::collections::HashMap;
47/// use tcrm_monitor::monitor::{TaskMonitor, config::TaskSpec, error::TaskMonitorError};
48/// use tcrm_task::tasks::config::TaskConfig;
49///
50/// let mut tasks = HashMap::new();
51/// tasks.insert(
52///     "build".to_string(),
53///     TaskSpec::new(TaskConfig::new("cargo").args(["build"]))
54///         .dependencies(["nonexistent"])
55/// );
56///
57/// match TaskMonitor::new(tasks) {
58///     Err(TaskMonitorError::DependencyNotFound { task_name, dependency_task_name }) => {
59///         println!("Task '{}' depends on missing task '{}'", task_name, dependency_task_name);
60///         panic!("Expected missing dependency error");
61///     }
62///     _ => panic!("Expected error"),
63/// }
64/// ```
65#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
66#[derive(Error, Debug, Clone, PartialEq)]
67pub enum TaskMonitorError {
68    /// Configuration parsing failed.
69    ///
70    /// This error occurs when task configuration cannot be parsed or contains invalid values.
71    ///
72    /// # Examples
73    ///
74    /// ```rust
75    /// use tcrm_monitor::monitor::error::TaskMonitorError;
76    ///
77    /// let error = TaskMonitorError::ConfigParse("Invalid timeout value".to_string());
78    /// assert!(error.to_string().contains("Config parse error"));
79    /// ```
80    #[error("Config parse error: {0}")]
81    ConfigParse(String),
82
83    /// A circular dependency was detected in the task graph.
84    ///
85    /// Contains the name of a task involved in the circular dependency.
86    ///
87    /// # Examples
88    ///
89    /// ```rust
90    /// use tcrm_monitor::monitor::error::TaskMonitorError;
91    ///
92    /// let error = TaskMonitorError::CircularDependency("task_a".to_string());
93    /// assert!(error.to_string().contains("Circular dependency"));
94    /// ```
95    #[error("Circular dependency detected involving task '{0}'")]
96    CircularDependency(String),
97
98    /// A task depends on another task that doesn't exist.
99    ///
100    /// This error is returned during task graph validation when a dependency
101    /// is specified that doesn't correspond to any defined task.
102    ///
103    /// # Examples
104    ///
105    /// ```rust
106    /// use tcrm_monitor::monitor::error::TaskMonitorError;
107    ///
108    /// let error = TaskMonitorError::DependencyNotFound {
109    ///     task_name: "build".to_string(),
110    ///     dependency_task_name: "nonexistent".to_string()
111    /// };
112    /// assert!(error.to_string().contains("not found"));
113    /// ```
114    #[error("Dependency '{dependency_task_name}' not found for task '{task_name}'")]
115    DependencyNotFound {
116        /// Name of the missing dependency task
117        dependency_task_name: String,
118        /// Name of the task that has the missing dependency
119        task_name: String,
120    },
121
122    #[error("Control error: {0}")]
123    ControlError(ControlCommandError),
124}
125
126#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
127#[derive(Error, Debug, Clone, PartialEq)]
128pub enum ControlCommandError {
129    #[error("Failed to terminate all tasks: {reason}")]
130    TerminateAllTasks { reason: String },
131    #[error("Failed to terminate task '{task_name}': {reason}")]
132    TerminateTask { task_name: String, reason: String },
133    #[error("Failed to send stdin to task '{task_name}': {reason}")]
134    SendStdin {
135        task_name: String,
136        input: String,
137        reason: SendStdinErrorReason,
138    },
139}
140
141/// Reasons why sending stdin to a task might fail.
142///
143/// These errors provide specific context about stdin operation failures,
144/// helping users understand why their stdin input couldn't be delivered.
145///
146/// # Examples
147///
148/// ```rust
149/// use tcrm_monitor::monitor::error::SendStdinErrorReason;
150///
151/// let error = SendStdinErrorReason::TaskNotFound;
152/// println!("Error: {}", error);
153/// ```
154#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
155#[derive(Error, Debug, Clone, PartialEq)]
156pub enum SendStdinErrorReason {
157    /// The specified task does not exist in the task collection.
158    ///
159    /// # Examples
160    ///
161    /// ```rust
162    /// use tcrm_monitor::monitor::error::SendStdinErrorReason;
163    ///
164    /// let error = SendStdinErrorReason::TaskNotFound;
165    /// assert!(error.to_string().contains("Task not found"));
166    /// ```
167    #[error("Task not found")]
168    TaskNotFound,
169
170    /// The task exists but does not have stdin enabled.
171    ///
172    /// Tasks must be configured with `enable_stdin(true)` to receive input.
173    ///
174    /// # Examples
175    ///
176    /// ```rust
177    /// use tcrm_monitor::monitor::error::SendStdinErrorReason;
178    ///
179    /// let error = SendStdinErrorReason::StdinNotEnabled;
180    /// assert!(error.to_string().contains("does not have stdin enabled"));
181    /// ```
182    #[error("Task does not have stdin enabled")]
183    StdinNotEnabled,
184
185    /// The task is not in a state that can receive stdin input.
186    ///
187    /// For example, the task might not be running yet or has already finished.
188    ///
189    /// # Examples
190    ///
191    /// ```rust
192    /// use tcrm_monitor::monitor::error::SendStdinErrorReason;
193    ///
194    /// let error = SendStdinErrorReason::TaskNotActive;
195    /// assert!(error.to_string().contains("not in a state"));
196    /// ```
197    #[error("Task is not in a state that can receive stdin")]
198    TaskNotActive,
199
200    /// The stdin channel for the task is closed or unavailable.
201    ///
202    /// This typically happens when the task has terminated or crashed.
203    ///
204    /// # Examples
205    ///
206    /// ```rust
207    /// use tcrm_monitor::monitor::error::SendStdinErrorReason;
208    ///
209    /// let error = SendStdinErrorReason::ChannelClosed;
210    /// assert!(error.to_string().contains("channel closed"));
211    /// ```
212    #[error("Failed to send stdin to task: channel closed")]
213    ChannelClosed,
214}