use axum::Json;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use serde_json::json;
use std::fmt;
/// Custom error type for the application
#[derive(Debug)]
pub enum AppError {
/// Serialization/deserialization errors
Serde(serde_json::Error),
/// MCP client errors
Mcp(rmcp::ServiceError),
/// System time errors
SystemTime(std::time::SystemTimeError),
/// I/O errors
Io(std::io::Error),
/// LLM client errors
Llm(String),
/// No data found errors
NoDataFound(String),
/// Database errors
DatabaseError(String),
// /// HTTP client errors (reqwest)
// Http(reqwest::Error),
// /// API key missing or invalid
// ApiKey(String),
// /// Search service errors
// Search(String),
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppError::Serde(err) => write!(f, "Serialization error: {err}"),
AppError::Mcp(err) => write!(f, "MCP error: {err}"),
AppError::SystemTime(err) => write!(f, "System time error: {err}"),
AppError::Io(err) => write!(f, "I/O error: {err}"),
AppError::Llm(msg) => write!(f, "LLM error: {msg}"),
AppError::NoDataFound(msg) => write!(f, "No data found: {msg}"),
AppError::DatabaseError(msg) => write!(f, "Database error: {msg}"),
// AppError::Http(err) => write!(f, "HTTP error: {}", err),
// AppError::ApiKey(msg) => write!(f, "API key error: {}", msg),
// AppError::Search(msg) => write!(f, "Search error: {}", msg),
}
}
}
impl std::error::Error for AppError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
AppError::Serde(err) => Some(err),
AppError::Mcp(err) => Some(err),
AppError::SystemTime(err) => Some(err),
AppError::Io(err) => Some(err),
AppError::Llm(_) => None,
AppError::NoDataFound(_) => None,
AppError::DatabaseError(_) => None,
// AppError::Http(err) => Some(err),
// _ => None,
}
}
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, error_message) = match &self {
AppError::Serde(_) => (StatusCode::BAD_REQUEST, "Invalid JSON format"),
AppError::Mcp(_) => (StatusCode::BAD_GATEWAY, "Search service error"),
AppError::SystemTime(_) => (StatusCode::INTERNAL_SERVER_ERROR, "System time error"),
AppError::Io(_) => (StatusCode::INTERNAL_SERVER_ERROR, "I/O error"),
AppError::Llm(_) => (StatusCode::INTERNAL_SERVER_ERROR, "LLM service error"),
AppError::NoDataFound(_) => (StatusCode::NOT_FOUND, "No data found"),
AppError::DatabaseError(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Database error"),
// AppError::Http(_) => (StatusCode::BAD_GATEWAY, "External service error"),
// AppError::ApiKey(_) => (StatusCode::UNAUTHORIZED, "Invalid API key"),
// AppError::Search(_) => (StatusCode::BAD_GATEWAY, "Search service error"),
};
let body = Json(json!({
"error": error_message,
"details": self.to_string()
}));
(status, body).into_response()
}
}
// Conversion implementations for common error types
impl From<serde_json::Error> for AppError {
fn from(err: serde_json::Error) -> Self {
AppError::Serde(err)
}
}
impl From<rmcp::ServiceError> for AppError {
fn from(err: rmcp::ServiceError) -> Self {
AppError::Mcp(err)
}
}
impl From<std::time::SystemTimeError> for AppError {
fn from(err: std::time::SystemTimeError) -> Self {
AppError::SystemTime(err)
}
}
impl From<std::io::Error> for AppError {
fn from(err: std::io::Error) -> Self {
AppError::Io(err)
}
}
impl From<McpError> for AppError {
fn from(err: McpError) -> Self {
match err {
McpError::Http(http_err) => AppError::Llm(format!("HTTP error: {http_err}")),
McpError::Internal(msg) => AppError::Llm(format!("Internal error: {msg}")),
McpError::Io(io_err) => AppError::Io(io_err),
// McpError::Parse(msg) => AppError::Llm(format!("Parse error: {}", msg)),
}
}
}
/// Convenient type alias for Results using AppError
pub type AppResult<T> = Result<T, AppError>;
/// Error type specific to MCP operations
#[derive(Debug)]
pub enum McpError {
/// Internal server errors
Internal(String),
/// HTTP request errors
Http(reqwest::Error),
/// I/O errors
Io(std::io::Error),
// /// Parse errors
// Parse(String),
}
impl fmt::Display for McpError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
McpError::Internal(msg) => write!(f, "Internal error: {msg}"),
McpError::Http(err) => write!(f, "HTTP error: {err}"),
McpError::Io(err) => write!(f, "I/O error: {err}"),
// McpError::Parse(msg) => write!(f, "Parse error: {}", msg),
}
}
}
impl std::error::Error for McpError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
McpError::Internal(_) => None,
McpError::Http(err) => Some(err),
McpError::Io(err) => Some(err),
// McpError::Parse(_) => None,
}
}
}
impl From<reqwest::Error> for McpError {
fn from(err: reqwest::Error) -> Self {
McpError::Http(err)
}
}
impl From<std::io::Error> for McpError {
fn from(err: std::io::Error) -> Self {
McpError::Io(err)
}
}
// impl From<reqwest::Error> for AppError {
// fn from(err: reqwest::Error) -> Self {
// AppError::Http(err)
// }
// }
// /// Helper functions for creating common errors
// impl AppError {
// pub fn mcp(msg: impl Into<String>) -> Self {
// AppError::Mcp(msg.into())
// }
// pub fn search(msg: impl Into<String>) -> Self {
// AppError::Search(msg.into())
// }
// pub fn api_key(msg: impl Into<String>) -> Self {
// AppError::ApiKey(msg.into())
// }
// }