Skip to main content

noetl_tools/
error.rs

1//! Tool execution error types.
2
3use thiserror::Error;
4
5/// Errors that can occur during tool execution.
6#[derive(Debug, Error)]
7pub enum ToolError {
8    /// Tool not found in registry.
9    #[error("Tool not found: {0}")]
10    NotFound(String),
11
12    /// Tool execution failed.
13    #[error("Execution failed: {0}")]
14    ExecutionFailed(String),
15
16    /// Tool execution timed out.
17    #[error("Execution timed out after {0} seconds")]
18    Timeout(u64),
19
20    /// Configuration error.
21    #[error("Configuration error: {0}")]
22    Configuration(String),
23
24    /// Template rendering error.
25    #[error("Template error: {0}")]
26    Template(String),
27
28    /// HTTP request error.
29    #[error("HTTP error: {0}")]
30    Http(String),
31
32    /// Database error.
33    #[error("Database error: {0}")]
34    Database(String),
35
36    /// Authentication error.
37    #[error("Authentication error: {0}")]
38    Auth(String),
39
40    /// Process spawn error.
41    #[error("Process error: {0}")]
42    Process(String),
43
44    /// JSON serialization/deserialization error.
45    #[error("JSON error: {0}")]
46    Json(String),
47
48    /// I/O error.
49    #[error("I/O error: {0}")]
50    Io(String),
51
52    /// Script evaluation error.
53    #[error("Script error: {0}")]
54    Script(String),
55}
56
57impl From<std::io::Error> for ToolError {
58    fn from(e: std::io::Error) -> Self {
59        ToolError::Io(e.to_string())
60    }
61}
62
63/// Bridge the lean `noetl-directives` error (noetl/ai-meta#92) into the
64/// tool-execution channel so a `DirectiveSpec::parse(...)?` inside a
65/// `Result<_, ToolError>` context maps cleanly.
66impl From<noetl_directives::DirectiveError> for ToolError {
67    fn from(e: noetl_directives::DirectiveError) -> Self {
68        match e {
69            noetl_directives::DirectiveError::Configuration(m) => ToolError::Configuration(m),
70        }
71    }
72}
73
74impl From<serde_json::Error> for ToolError {
75    fn from(e: serde_json::Error) -> Self {
76        ToolError::Json(e.to_string())
77    }
78}
79
80impl From<reqwest::Error> for ToolError {
81    fn from(e: reqwest::Error) -> Self {
82        ToolError::Http(e.to_string())
83    }
84}
85
86impl From<minijinja::Error> for ToolError {
87    fn from(e: minijinja::Error) -> Self {
88        ToolError::Template(e.to_string())
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn test_error_display() {
98        let err = ToolError::NotFound("shell".to_string());
99        assert_eq!(err.to_string(), "Tool not found: shell");
100
101        let err = ToolError::Timeout(30);
102        assert_eq!(err.to_string(), "Execution timed out after 30 seconds");
103    }
104
105    #[test]
106    fn test_error_from_io() {
107        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
108        let tool_err: ToolError = io_err.into();
109        assert!(matches!(tool_err, ToolError::Io(_)));
110    }
111}