ultrafast_mcp_sequential_thinking/thinking/
error.rs

1//! # Error Handling Module
2//!
3//! Comprehensive error handling for the sequential thinking functionality.
4//!
5//! This module provides error types and handling for all aspects of the
6//! sequential thinking process, including validation errors, processing
7//! errors, and system errors.
8
9use thiserror::Error;
10
11/// Main error type for sequential thinking operations
12#[derive(Error, Debug)]
13pub enum SequentialThinkingError {
14    /// Invalid thought data
15    #[error("Invalid thought data: {message}")]
16    InvalidThoughtData { message: String },
17
18    /// Thought processing error
19    #[error("Thought processing error: {message}")]
20    ProcessingError { message: String },
21
22    /// Session management error
23    #[error("Session error: {message}")]
24    SessionError { message: String },
25
26    /// Branch management error
27    #[error("Branch error: {message}")]
28    BranchError { message: String },
29
30    /// Validation error
31    #[error("Validation error: {message}")]
32    ValidationError { message: String },
33
34    /// Configuration error
35    #[error("Configuration error: {message}")]
36    ConfigError { message: String },
37
38    /// Serialization/deserialization error
39    #[error("Serialization error: {message}")]
40    SerializationError { message: String },
41
42    /// Network/transport error
43    #[error("Transport error: {message}")]
44    TransportError { message: String },
45
46    /// Internal system error
47    #[error("Internal error: {message}")]
48    InternalError { message: String },
49
50    /// Resource not found
51    #[error("Resource not found: {resource}")]
52    NotFound { resource: String },
53
54    /// Permission denied
55    #[error("Permission denied: {reason}")]
56    PermissionDenied { reason: String },
57
58    /// Rate limiting error
59    #[error("Rate limit exceeded: {limit}")]
60    RateLimitExceeded { limit: String },
61
62    /// Timeout error
63    #[error("Operation timed out after {duration:?}")]
64    Timeout { duration: std::time::Duration },
65
66    /// Cancellation error
67    #[error("Operation was cancelled: {reason}")]
68    Cancelled { reason: String },
69
70    /// Wrapped error from underlying dependencies
71    #[error("Wrapped error: {source}")]
72    Wrapped {
73        #[from]
74        source: Box<dyn std::error::Error + Send + Sync>,
75    },
76}
77
78impl SequentialThinkingError {
79    /// Create an invalid thought data error
80    pub fn invalid_thought_data(message: impl Into<String>) -> Self {
81        Self::InvalidThoughtData {
82            message: message.into(),
83        }
84    }
85
86    /// Create a processing error
87    pub fn processing_error(message: impl Into<String>) -> Self {
88        Self::ProcessingError {
89            message: message.into(),
90        }
91    }
92
93    /// Create a session error
94    pub fn session_error(message: impl Into<String>) -> Self {
95        Self::SessionError {
96            message: message.into(),
97        }
98    }
99
100    /// Create a branch error
101    pub fn branch_error(message: impl Into<String>) -> Self {
102        Self::BranchError {
103            message: message.into(),
104        }
105    }
106
107    /// Create a validation error
108    pub fn validation_error(message: impl Into<String>) -> Self {
109        Self::ValidationError {
110            message: message.into(),
111        }
112    }
113
114    /// Create a configuration error
115    pub fn config_error(message: impl Into<String>) -> Self {
116        Self::ConfigError {
117            message: message.into(),
118        }
119    }
120
121    /// Create a serialization error
122    pub fn serialization_error(message: impl Into<String>) -> Self {
123        Self::SerializationError {
124            message: message.into(),
125        }
126    }
127
128    /// Create a transport error
129    pub fn transport_error(message: impl Into<String>) -> Self {
130        Self::TransportError {
131            message: message.into(),
132        }
133    }
134
135    /// Create an internal error
136    pub fn internal_error(message: impl Into<String>) -> Self {
137        Self::InternalError {
138            message: message.into(),
139        }
140    }
141
142    /// Create a not found error
143    pub fn not_found(resource: impl Into<String>) -> Self {
144        Self::NotFound {
145            resource: resource.into(),
146        }
147    }
148
149    /// Create a permission denied error
150    pub fn permission_denied(reason: impl Into<String>) -> Self {
151        Self::PermissionDenied {
152            reason: reason.into(),
153        }
154    }
155
156    /// Create a rate limit error
157    pub fn rate_limit_exceeded(limit: impl Into<String>) -> Self {
158        Self::RateLimitExceeded {
159            limit: limit.into(),
160        }
161    }
162
163    /// Create a timeout error
164    pub fn timeout(duration: std::time::Duration) -> Self {
165        Self::Timeout { duration }
166    }
167
168    /// Create a cancellation error
169    pub fn cancelled(reason: impl Into<String>) -> Self {
170        Self::Cancelled {
171            reason: reason.into(),
172        }
173    }
174
175    /// Check if this is a retryable error
176    pub fn is_retryable(&self) -> bool {
177        matches!(
178            self,
179            Self::TransportError { .. } | Self::Timeout { .. } | Self::RateLimitExceeded { .. }
180        )
181    }
182
183    /// Check if this is a client error (not retryable)
184    pub fn is_client_error(&self) -> bool {
185        matches!(
186            self,
187            Self::InvalidThoughtData { .. }
188                | Self::ValidationError { .. }
189                | Self::ConfigError { .. }
190                | Self::NotFound { .. }
191                | Self::PermissionDenied { .. }
192        )
193    }
194
195    /// Check if this is a server error (potentially retryable)
196    pub fn is_server_error(&self) -> bool {
197        matches!(
198            self,
199            Self::ProcessingError { .. }
200                | Self::SessionError { .. }
201                | Self::BranchError { .. }
202                | Self::InternalError { .. }
203                | Self::SerializationError { .. }
204        )
205    }
206
207    /// Get a user-friendly error message
208    pub fn user_message(&self) -> String {
209        match self {
210            Self::InvalidThoughtData { message } => {
211                format!("Invalid thought data: {message}")
212            }
213            Self::ProcessingError { message } => {
214                format!("Failed to process thought: {message}")
215            }
216            Self::SessionError { message } => {
217                format!("Session error: {message}")
218            }
219            Self::BranchError { message } => {
220                format!("Branch error: {message}")
221            }
222            Self::ValidationError { message } => {
223                format!("Validation failed: {message}")
224            }
225            Self::ConfigError { message } => {
226                format!("Configuration error: {message}")
227            }
228            Self::SerializationError { message } => {
229                format!("Data format error: {message}")
230            }
231            Self::TransportError { message } => {
232                format!("Connection error: {message}")
233            }
234            Self::InternalError { message } => {
235                format!("System error: {message}")
236            }
237            Self::NotFound { resource } => {
238                format!("Resource not found: {resource}")
239            }
240            Self::PermissionDenied { reason } => {
241                format!("Access denied: {reason}")
242            }
243            Self::RateLimitExceeded { limit } => {
244                format!("Too many requests: {limit}")
245            }
246            Self::Timeout { duration } => {
247                format!("Operation timed out after {duration:?}")
248            }
249            Self::Cancelled { reason } => {
250                format!("Operation cancelled: {reason}")
251            }
252            Self::Wrapped { source } => {
253                format!("Error: {source}")
254            }
255        }
256    }
257
258    /// Get error code for API responses
259    pub fn error_code(&self) -> &'static str {
260        match self {
261            Self::InvalidThoughtData { .. } => "INVALID_THOUGHT_DATA",
262            Self::ProcessingError { .. } => "PROCESSING_ERROR",
263            Self::SessionError { .. } => "SESSION_ERROR",
264            Self::BranchError { .. } => "BRANCH_ERROR",
265            Self::ValidationError { .. } => "VALIDATION_ERROR",
266            Self::ConfigError { .. } => "CONFIG_ERROR",
267            Self::SerializationError { .. } => "SERIALIZATION_ERROR",
268            Self::TransportError { .. } => "TRANSPORT_ERROR",
269            Self::InternalError { .. } => "INTERNAL_ERROR",
270            Self::NotFound { .. } => "NOT_FOUND",
271            Self::PermissionDenied { .. } => "PERMISSION_DENIED",
272            Self::RateLimitExceeded { .. } => "RATE_LIMIT_EXCEEDED",
273            Self::Timeout { .. } => "TIMEOUT",
274            Self::Cancelled { .. } => "CANCELLED",
275            Self::Wrapped { .. } => "WRAPPED_ERROR",
276        }
277    }
278}
279
280/// Result type for sequential thinking operations
281pub type SequentialThinkingResult<T> = Result<T, SequentialThinkingError>;
282
283/// Error context for adding additional information to errors
284#[derive(Debug, Clone)]
285pub struct ErrorContext {
286    /// Operation being performed
287    pub operation: String,
288    /// Additional context information
289    pub context: std::collections::HashMap<String, String>,
290    /// Timestamp when error occurred
291    pub timestamp: chrono::DateTime<chrono::Utc>,
292}
293
294impl ErrorContext {
295    /// Create a new error context
296    pub fn new(operation: impl Into<String>) -> Self {
297        Self {
298            operation: operation.into(),
299            context: std::collections::HashMap::new(),
300            timestamp: chrono::Utc::now(),
301        }
302    }
303
304    /// Add context information
305    pub fn with_context(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
306        self.context.insert(key.into(), value.into());
307        self
308    }
309
310    /// Add multiple context items
311    pub fn with_contexts(mut self, contexts: Vec<(String, String)>) -> Self {
312        for (key, value) in contexts {
313            self.context.insert(key, value);
314        }
315        self
316    }
317}
318
319/// Error handling utilities
320pub mod utils {
321    use super::*;
322
323    /// Convert a string error to a SequentialThinkingError
324    pub fn from_string_error(error: String) -> SequentialThinkingError {
325        SequentialThinkingError::InternalError { message: error }
326    }
327
328    /// Convert a generic error to a SequentialThinkingError
329    pub fn from_generic_error<E: std::error::Error + Send + Sync + 'static>(
330        error: E,
331    ) -> SequentialThinkingError {
332        SequentialThinkingError::Wrapped {
333            source: Box::new(error),
334        }
335    }
336
337    /// Create a timeout error with a specific duration
338    pub fn timeout_error(duration: std::time::Duration) -> SequentialThinkingError {
339        SequentialThinkingError::Timeout { duration }
340    }
341
342    /// Create a validation error for a specific field
343    pub fn field_validation_error(field: &str, message: &str) -> SequentialThinkingError {
344        SequentialThinkingError::ValidationError {
345            message: format!("Field '{field}': {message}"),
346        }
347    }
348
349    /// Create a required field error
350    pub fn required_field_error(field: &str) -> SequentialThinkingError {
351        field_validation_error(field, "Field is required")
352    }
353
354    /// Create an invalid format error
355    pub fn invalid_format_error(field: &str, expected: &str) -> SequentialThinkingError {
356        field_validation_error(field, &format!("Expected format: {expected}"))
357    }
358}
359
360// Implement From for common error types
361impl From<std::io::Error> for SequentialThinkingError {
362    fn from(err: std::io::Error) -> Self {
363        Self::TransportError {
364            message: err.to_string(),
365        }
366    }
367}
368
369impl From<serde_json::Error> for SequentialThinkingError {
370    fn from(err: serde_json::Error) -> Self {
371        Self::SerializationError {
372            message: err.to_string(),
373        }
374    }
375}
376
377impl From<uuid::Error> for SequentialThinkingError {
378    fn from(err: uuid::Error) -> Self {
379        Self::ValidationError {
380            message: err.to_string(),
381        }
382    }
383}
384
385impl From<chrono::ParseError> for SequentialThinkingError {
386    fn from(err: chrono::ParseError) -> Self {
387        Self::ValidationError {
388            message: err.to_string(),
389        }
390    }
391}
392
393#[cfg(test)]
394mod tests {
395    use super::*;
396
397    #[test]
398    fn test_error_creation() {
399        let error = SequentialThinkingError::invalid_thought_data("Empty thought");
400        assert!(matches!(
401            error,
402            SequentialThinkingError::InvalidThoughtData { .. }
403        ));
404        assert!(error.is_client_error());
405        assert!(!error.is_retryable());
406    }
407
408    #[test]
409    fn test_error_codes() {
410        let error = SequentialThinkingError::processing_error("Test");
411        assert_eq!(error.error_code(), "PROCESSING_ERROR");
412    }
413
414    #[test]
415    fn test_user_message() {
416        let error = SequentialThinkingError::validation_error("Invalid input");
417        let message = error.user_message();
418        assert!(message.contains("Validation failed"));
419        assert!(message.contains("Invalid input"));
420    }
421
422    #[test]
423    fn test_error_context() {
424        let context = ErrorContext::new("test_operation")
425            .with_context("user_id", "123")
426            .with_context("session_id", "abc");
427
428        assert_eq!(context.operation, "test_operation");
429        assert_eq!(context.context.get("user_id"), Some(&"123".to_string()));
430        assert_eq!(context.context.get("session_id"), Some(&"abc".to_string()));
431    }
432
433    #[test]
434    fn test_from_implementations() {
435        let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "File not found");
436        let mcp_error: SequentialThinkingError = io_error.into();
437        assert!(matches!(
438            mcp_error,
439            SequentialThinkingError::TransportError { .. }
440        ));
441
442        let json_error = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
443        let mcp_error: SequentialThinkingError = json_error.into();
444        assert!(matches!(
445            mcp_error,
446            SequentialThinkingError::SerializationError { .. }
447        ));
448    }
449}