turbomcp_cli/
error.rs

1//! Enhanced error types for CLI operations
2
3use std::fmt;
4use thiserror::Error;
5
6/// CLI-specific errors with rich context
7#[derive(Error, Debug)]
8#[non_exhaustive]
9pub enum CliError {
10    /// Transport layer errors
11    #[error("Transport error: {0}")]
12    Transport(#[from] turbomcp_protocol::Error),
13
14    /// Invalid command arguments
15    #[error("Invalid arguments: {0}")]
16    InvalidArguments(String),
17
18    /// Server returned an error
19    #[error("Server error [{code}]: {message}")]
20    ServerError { code: i32, message: String },
21
22    /// Operation timed out
23    #[error("Operation '{operation}' timed out after {elapsed:?}")]
24    Timeout {
25        operation: String,
26        elapsed: std::time::Duration,
27    },
28
29    /// Client not initialized
30    #[error("Client not initialized - call 'initialize' first")]
31    NotInitialized,
32
33    /// JSON parsing error
34    #[error("JSON error: {0}")]
35    Json(#[from] serde_json::Error),
36
37    /// YAML parsing error
38    #[error("YAML error: {0}")]
39    Yaml(#[from] serde_yaml::Error),
40
41    /// I/O error
42    #[error("I/O error: {0}")]
43    Io(#[from] std::io::Error),
44
45    /// Configuration error
46    #[error("Config error: {0}")]
47    Config(#[from] config::ConfigError),
48
49    /// Connection failed
50    #[error("Connection failed: {0}")]
51    ConnectionFailed(String),
52
53    /// Feature not supported
54    #[error("Feature not supported: {0}")]
55    NotSupported(String),
56
57    /// Generic error
58    #[error("{0}")]
59    Other(String),
60}
61
62impl CliError {
63    /// Get user-friendly suggestions for resolving the error
64    pub fn suggestions(&self) -> Vec<&'static str> {
65        match self {
66            Self::ConnectionFailed(_) => vec![
67                "Check if the server is running",
68                "Verify the connection URL",
69                "Use --transport to specify transport explicitly",
70            ],
71            Self::NotInitialized => vec![
72                "Ensure the server is started before calling operations",
73                "Check server logs for initialization errors",
74            ],
75            Self::Timeout { .. } => vec![
76                "Increase timeout with --timeout flag",
77                "Check server responsiveness",
78                "Verify network connectivity",
79            ],
80            Self::InvalidArguments(_) => vec![
81                "Check argument format (must be valid JSON)",
82                "Use --help to see expected format",
83            ],
84            _ => vec![],
85        }
86    }
87
88    /// Get the error category for colored output
89    pub fn category(&self) -> ErrorCategory {
90        match self {
91            Self::Transport(_) | Self::ConnectionFailed(_) => ErrorCategory::Connection,
92            Self::InvalidArguments(_) => ErrorCategory::User,
93            Self::ServerError { .. } => ErrorCategory::Server,
94            Self::Timeout { .. } => ErrorCategory::Timeout,
95            Self::Json(_) | Self::Yaml(_) => ErrorCategory::Parsing,
96            Self::Io(_) => ErrorCategory::System,
97            Self::Config(_) => ErrorCategory::Config,
98            Self::NotSupported(_) => ErrorCategory::NotSupported,
99            _ => ErrorCategory::Other,
100        }
101    }
102}
103
104/// Error categories for colored output
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106pub enum ErrorCategory {
107    Connection,
108    User,
109    Server,
110    Timeout,
111    Parsing,
112    System,
113    Config,
114    NotSupported,
115    Other,
116}
117
118impl fmt::Display for ErrorCategory {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        match self {
121            Self::Connection => write!(f, "Connection"),
122            Self::User => write!(f, "User Input"),
123            Self::Server => write!(f, "Server"),
124            Self::Timeout => write!(f, "Timeout"),
125            Self::Parsing => write!(f, "Parsing"),
126            Self::System => write!(f, "System"),
127            Self::Config => write!(f, "Configuration"),
128            Self::NotSupported => write!(f, "Not Supported"),
129            Self::Other => write!(f, "Error"),
130        }
131    }
132}
133
134/// Helper for creating CliError from strings
135impl From<String> for CliError {
136    fn from(s: String) -> Self {
137        Self::Other(s)
138    }
139}
140
141impl From<&str> for CliError {
142    fn from(s: &str) -> Self {
143        Self::Other(s.to_string())
144    }
145}
146
147impl From<Box<turbomcp_protocol::Error>> for CliError {
148    fn from(err: Box<turbomcp_protocol::Error>) -> Self {
149        Self::Transport(*err)
150    }
151}
152
153/// Result type for CLI operations
154pub type CliResult<T> = Result<T, CliError>;