1use thiserror::Error;
13
14#[derive(Debug, Error)]
25pub enum ToolError {
26 #[error("Tool '{name}' not found in registry")]
28 NotFound {
29 name: String,
31 },
32
33 #[error("Invalid arguments for '{tool}': {reason}")]
35 InvalidArguments {
36 tool: String,
38 reason: String,
40 },
41
42 #[error("Execution of '{tool}' failed: {message}")]
44 ExecutionFailed {
45 tool: String,
47 message: String,
49 },
50
51 #[error("Tool '{tool}' timed out after {timeout_ms}ms")]
53 Timeout {
54 tool: String,
56 timeout_ms: u64,
58 },
59
60 #[error("Serialization error: {0}")]
62 Serialization(#[from] serde_json::Error),
63
64 #[error("Audit logging failed: {0}")]
66 AuditFailed(String),
67
68 #[error("Tool '{name}' is currently unavailable: {reason}")]
70 Unavailable {
71 name: String,
73 reason: String,
75 },
76}
77
78impl ToolError {
79 pub fn not_found(name: impl Into<String>) -> Self {
87 Self::NotFound { name: name.into() }
88 }
89
90 pub fn invalid_args(tool: impl Into<String>, reason: impl Into<String>) -> Self {
95 Self::InvalidArguments {
96 tool: tool.into(),
97 reason: reason.into(),
98 }
99 }
100
101 pub fn execution_failed(tool: impl Into<String>, message: impl Into<String>) -> Self {
106 Self::ExecutionFailed {
107 tool: tool.into(),
108 message: message.into(),
109 }
110 }
111
112 pub fn timeout(tool: impl Into<String>, timeout_ms: u64) -> Self {
114 Self::Timeout {
115 tool: tool.into(),
116 timeout_ms,
117 }
118 }
119
120 pub fn unavailable(name: impl Into<String>, reason: impl Into<String>) -> Self {
122 Self::Unavailable {
123 name: name.into(),
124 reason: reason.into(),
125 }
126 }
127
128 pub fn is_retryable(&self) -> bool {
130 matches!(self, Self::Timeout { .. } | Self::Unavailable { .. })
131 }
132
133 pub fn should_audit(&self) -> bool {
135 !matches!(self, Self::AuditFailed(_))
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn test_error_display() {
146 let err = ToolError::not_found("calculator");
147 assert!(err.to_string().contains("calculator"));
148 assert!(err.to_string().contains("not found"));
149 }
150
151 #[test]
152 fn test_invalid_args() {
153 let err = ToolError::invalid_args("datetime", "Missing timezone field");
154 assert!(err.to_string().contains("datetime"));
155 assert!(err.to_string().contains("Missing timezone"));
156 }
157
158 #[test]
159 fn test_retryable() {
160 assert!(ToolError::timeout("test", 1000).is_retryable());
161 assert!(ToolError::unavailable("test", "maintenance").is_retryable());
162 assert!(!ToolError::not_found("test").is_retryable());
163 }
164
165 #[test]
166 fn test_should_audit() {
167 assert!(ToolError::not_found("test").should_audit());
168 assert!(!ToolError::AuditFailed("db error".into()).should_audit());
169 }
170}