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}