1use riglr_macros::IntoToolError;
4use thiserror::Error;
5
6#[derive(Error, Debug, IntoToolError)]
12pub enum WebToolError {
13 #[error("Network error: {0}")]
15 Network(String),
16
17 #[error("HTTP error: {0}")]
19 Http(#[from] reqwest::Error),
20
21 #[error("API error: {0}")]
23 Api(String),
24
25 #[error("Rate limit exceeded: {0}")]
27 #[tool_error(rate_limited)]
28 RateLimit(String),
29
30 #[error("Authentication error: {0}")]
32 #[tool_error(permanent)]
33 Auth(String),
34
35 #[error("Parsing error: {0}")]
37 #[tool_error(permanent)]
38 Parsing(String),
39
40 #[error("Serialization error: {0}")]
42 Serialization(#[from] serde_json::Error),
43
44 #[error("URL error: {0}")]
46 #[tool_error(permanent)]
47 Url(#[from] url::ParseError),
48
49 #[error("Configuration error: {0}")]
51 #[tool_error(permanent)]
52 Config(String),
53
54 #[error("Client error: {0}")]
56 #[tool_error(permanent)]
57 Client(String),
58
59 #[error("Invalid input: {0}")]
61 #[tool_error(permanent)]
62 InvalidInput(String),
63
64 #[error("Core error: {0}")]
66 #[tool_error(permanent)]
67 Core(#[from] riglr_core::CoreError),
68}
69
70pub type Result<T> = std::result::Result<T, WebToolError>;
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use serde_json;
81 use std::error::Error;
82
83 #[test]
84 fn test_network_error_display() {
85 let error = WebToolError::Network("Connection failed".to_string());
86 assert_eq!(error.to_string(), "Network error: Connection failed");
87 }
88
89 #[test]
90 fn test_network_error_debug() {
91 let error = WebToolError::Network("Connection failed".to_string());
92 let debug_str = format!("{:?}", error);
93 assert!(debug_str.contains("Network"));
94 assert!(debug_str.contains("Connection failed"));
95 }
96
97 #[test]
98 fn test_http_error_conversion() {
99 let url_error = reqwest::Url::parse("not a valid url").unwrap_err();
105 let web_error = WebToolError::Url(url_error);
106 assert!(web_error.to_string().contains("URL error:"));
107
108 let mock_http_error = WebToolError::Network("HTTP connection failed".to_string());
112 assert!(mock_http_error
113 .to_string()
114 .contains("Network error: HTTP connection failed"));
115 }
116
117 #[test]
118 fn test_api_error_display() {
119 let error = WebToolError::Api("Invalid API key".to_string());
120 assert_eq!(error.to_string(), "API error: Invalid API key");
121 }
122
123 #[test]
124 fn test_rate_limit_error_display() {
125 let error = WebToolError::RateLimit("Too many requests".to_string());
126 assert_eq!(error.to_string(), "Rate limit exceeded: Too many requests");
127 }
128
129 #[test]
130 fn test_auth_error_display() {
131 let error = WebToolError::Auth("Invalid credentials".to_string());
132 assert_eq!(
133 error.to_string(),
134 "Authentication error: Invalid credentials"
135 );
136 }
137
138 #[test]
139 fn test_parsing_error_display() {
140 let error = WebToolError::Parsing("JSON malformed".to_string());
141 assert_eq!(error.to_string(), "Parsing error: JSON malformed");
142 }
143
144 #[test]
145 fn test_serialization_error_from_serde_json() {
146 let json_error = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
148 let error = WebToolError::from(json_error);
149
150 assert!(error.to_string().contains("Serialization error:"));
151 assert!(matches!(error, WebToolError::Serialization(_)));
152 }
153
154 #[test]
155 fn test_url_error_from_parse_error() {
156 let parse_error = url::Url::parse("not a valid url").unwrap_err();
157 let error = WebToolError::from(parse_error);
158
159 assert!(error.to_string().contains("URL error:"));
160 assert!(matches!(error, WebToolError::Url(_)));
161 }
162
163 #[test]
164 fn test_config_error_display() {
165 let error = WebToolError::Config("Missing configuration".to_string());
166 assert_eq!(
167 error.to_string(),
168 "Configuration error: Missing configuration"
169 );
170 }
171
172 #[test]
173 fn test_client_error_display() {
174 let error = WebToolError::Client("Failed to create client".to_string());
175 assert_eq!(error.to_string(), "Client error: Failed to create client");
176 }
177
178 #[test]
179 fn test_invalid_input_error_display() {
180 let error = WebToolError::InvalidInput("Empty parameter".to_string());
181 assert_eq!(error.to_string(), "Invalid input: Empty parameter");
182 }
183
184 #[test]
185 fn test_core_error_from_riglr_core() {
186 use riglr_core::CoreError;
190
191 let core_error = CoreError::Generic("test config error".to_string());
193 let error = WebToolError::from(core_error);
194
195 assert!(error.to_string().contains("Core error:"));
196 assert!(matches!(error, WebToolError::Core(_)));
197 }
198
199 #[test]
200 fn test_error_source_chain() {
201 let json_error = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
203 let error = WebToolError::from(json_error);
204
205 assert!(error.source().is_some());
207 }
208
209 #[test]
210 fn test_error_debug_impl() {
211 let errors = vec![
213 WebToolError::Network("test".to_string()),
214 WebToolError::Api("test".to_string()),
215 WebToolError::RateLimit("test".to_string()),
216 WebToolError::Auth("test".to_string()),
217 WebToolError::Parsing("test".to_string()),
218 WebToolError::Config("test".to_string()),
219 WebToolError::Client("test".to_string()),
220 WebToolError::InvalidInput("test".to_string()),
221 ];
222
223 for error in errors {
224 let debug_str = format!("{:?}", error);
225 assert!(!debug_str.is_empty());
226 }
227 }
228
229 #[test]
230 fn test_result_type_alias() {
231 let ok_result: Result<i32> = Ok(42);
233 let err_result: Result<i32> = Err(WebToolError::Network("test".to_string()));
234
235 assert!(ok_result.is_ok());
236 assert_eq!(ok_result.unwrap(), 42);
237
238 assert!(err_result.is_err());
239 assert!(matches!(err_result.unwrap_err(), WebToolError::Network(_)));
240 }
241
242 #[test]
243 fn test_error_equality() {
244 let error1 = WebToolError::Network("same message".to_string());
247 let error2 = WebToolError::Network("same message".to_string());
248 let error3 = WebToolError::Network("different message".to_string());
249
250 assert_eq!(error1.to_string(), error2.to_string());
251 assert_ne!(error1.to_string(), error3.to_string());
252 }
253
254 #[test]
255 fn test_empty_string_errors() {
256 let errors = vec![
258 WebToolError::Network("".to_string()),
259 WebToolError::Api("".to_string()),
260 WebToolError::RateLimit("".to_string()),
261 WebToolError::Auth("".to_string()),
262 WebToolError::Parsing("".to_string()),
263 WebToolError::Config("".to_string()),
264 WebToolError::Client("".to_string()),
265 WebToolError::InvalidInput("".to_string()),
266 ];
267
268 for error in errors {
269 let error_str = error.to_string();
270 assert!(!error_str.is_empty());
271 assert!(error_str.contains("error:"));
273 }
274 }
275
276 #[test]
277 fn test_very_long_error_messages() {
278 let long_message = "a".repeat(1000);
280 let error = WebToolError::Network(long_message.clone());
281 let error_str = error.to_string();
282
283 assert!(error_str.contains(&long_message));
284 assert!(error_str.len() > 1000);
285 }
286
287 #[test]
288 fn test_special_characters_in_error_messages() {
289 let special_message = "Error with special chars: 你好 🚀 \n\t\"quotes\"";
291 let error = WebToolError::InvalidInput(special_message.to_string());
292 let error_str = error.to_string();
293
294 assert!(error_str.contains(&special_message));
295 }
296
297 #[test]
298 fn test_error_is_send_and_sync() {
299 fn assert_send<T: Send>() {}
301 fn assert_sync<T: Sync>() {}
302
303 assert_send::<WebToolError>();
304 assert_sync::<WebToolError>();
305 }
306}