miyabi_claudable/
error.rs1use miyabi_types::error::{ErrorCode, UnifiedError};
4use std::any::Any;
5use thiserror::Error;
6
7#[derive(Debug, Error)]
9pub enum ClaudableError {
10 #[error("HTTP request failed: {0}")]
12 HttpError(#[from] reqwest::Error),
13
14 #[error("Claudable API error {0}: {1}")]
16 ApiError(u16, String),
17
18 #[error("Invalid response format: {0}")]
20 ParseError(String),
21
22 #[error("IO error: {0}")]
24 IoError(#[from] std::io::Error),
25
26 #[error("npm install failed: {0}")]
28 NpmInstallError(String),
29
30 #[error("Next.js build failed: {0}")]
32 BuildError(String),
33
34 #[error("Request timeout after {0}ms")]
36 Timeout(u64),
37
38 #[error("Configuration error: {0}")]
40 ConfigError(String),
41}
42
43pub type Result<T> = std::result::Result<T, ClaudableError>;
45
46impl UnifiedError for ClaudableError {
51 fn code(&self) -> ErrorCode {
52 match self {
53 Self::HttpError(_) => ErrorCode::HTTP_ERROR,
54 Self::ApiError(_, _) => ErrorCode::HTTP_ERROR,
55 Self::ParseError(_) => ErrorCode::PARSE_ERROR,
56 Self::IoError(e) => match e.kind() {
57 std::io::ErrorKind::NotFound => ErrorCode::FILE_NOT_FOUND,
58 std::io::ErrorKind::PermissionDenied => ErrorCode::PERMISSION_DENIED,
59 _ => ErrorCode::IO_ERROR,
60 },
61 Self::NpmInstallError(_) => ErrorCode::PROCESS_ERROR,
62 Self::BuildError(_) => ErrorCode::PROCESS_ERROR,
63 Self::Timeout(_) => ErrorCode::TIMEOUT_ERROR,
64 Self::ConfigError(_) => ErrorCode::CONFIG_ERROR,
65 }
66 }
67
68 fn user_message(&self) -> String {
69 match self {
70 Self::HttpError(e) => format!(
71 "Failed to communicate with Claudable API: {}. Please check your network connection and try again.",
72 e
73 ),
74 Self::ApiError(status, msg) => format!(
75 "Claudable API returned error {}: {}. Please check the API documentation or contact support.",
76 status, msg
77 ),
78 Self::Timeout(ms) => format!(
79 "Request to Claudable timed out after {}ms. The service may be experiencing high load. Please try again.",
80 ms
81 ),
82 Self::NpmInstallError(msg) => format!(
83 "Failed to install npm dependencies: {}. Please ensure npm is installed and you have necessary permissions.",
84 msg
85 ),
86 Self::BuildError(msg) => format!(
87 "Next.js build failed: {}. Please check your configuration and dependencies.",
88 msg
89 ),
90 Self::ConfigError(msg) => format!(
91 "Configuration error: {}. Please verify your Claudable settings.",
92 msg
93 ),
94 _ => self.to_string(),
96 }
97 }
98
99 fn context(&self) -> Option<&dyn Any> {
100 match self {
101 Self::ApiError(status, _) => Some(status as &dyn Any),
102 Self::Timeout(ms) => Some(ms as &dyn Any),
103 Self::NpmInstallError(msg) => Some(msg as &dyn Any),
104 Self::BuildError(msg) => Some(msg as &dyn Any),
105 _ => None,
106 }
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_error_display() {
116 let error = ClaudableError::ApiError(404, "Not Found".to_string());
117 assert_eq!(error.to_string(), "Claudable API error 404: Not Found");
118 }
119
120 #[test]
121 fn test_timeout_error() {
122 let error = ClaudableError::Timeout(30000);
123 assert_eq!(error.to_string(), "Request timeout after 30000ms");
124 }
125
126 #[test]
127 fn test_npm_install_error() {
128 let error = ClaudableError::NpmInstallError("EACCES: permission denied".to_string());
129 assert!(error.to_string().contains("npm install failed"));
130 }
131
132 #[test]
133 fn test_claudable_error_codes() {
134 let error = ClaudableError::ApiError(404, "Not Found".to_string());
135 assert_eq!(error.code(), ErrorCode::HTTP_ERROR);
136
137 let error = ClaudableError::Timeout(30000);
138 assert_eq!(error.code(), ErrorCode::TIMEOUT_ERROR);
139
140 let error = ClaudableError::NpmInstallError("error".to_string());
141 assert_eq!(error.code(), ErrorCode::PROCESS_ERROR);
142
143 let error = ClaudableError::BuildError("error".to_string());
144 assert_eq!(error.code(), ErrorCode::PROCESS_ERROR);
145 }
146
147 #[test]
148 fn test_user_messages() {
149 let error = ClaudableError::ApiError(500, "Internal Server Error".to_string());
150 let msg = error.user_message();
151 assert!(msg.contains("500"));
152 assert!(msg.contains("Internal Server Error"));
153
154 let error = ClaudableError::Timeout(5000);
155 let msg = error.user_message();
156 assert!(msg.contains("5000ms"));
157 assert!(msg.contains("timed out"));
158
159 let error = ClaudableError::NpmInstallError("EACCES".to_string());
160 let msg = error.user_message();
161 assert!(msg.contains("npm dependencies"));
162 assert!(msg.contains("EACCES"));
163 }
164
165 #[test]
166 fn test_context_extraction() {
167 let error = ClaudableError::ApiError(404, "Not Found".to_string());
168 assert!(error.context().is_some());
169
170 let error = ClaudableError::Timeout(30000);
171 assert!(error.context().is_some());
172
173 let error = ClaudableError::NpmInstallError("error".to_string());
174 assert!(error.context().is_some());
175
176 let error = ClaudableError::ParseError("invalid JSON".to_string());
177 assert!(error.context().is_none());
178 }
179}