Files
mcp-memory/server/src/error.rs
T

170 lines
5.3 KiB
Rust

use axum::{
Json,
http::StatusCode,
response::{IntoResponse, Response},
};
use serde_json::json;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, AppError>;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Not Found: {0}")]
NotFound(String),
#[error("Entity '{name}' was not found in namespace '{namespace}'")]
EntityNotFound { name: String, namespace: String },
#[error("Invalid relation between '{from}' and '{to}': {reason}")]
InvalidRelation {
from: String,
to: String,
reason: String,
},
#[error("Database error for key '{key}': {message}")]
DatabaseError { key: String, message: String },
#[error("Forbidden: {0}")]
Forbidden(String),
#[error("Internal Server Error: {0}")]
Internal(String),
#[error("Bad Request: {0}\n[SYSTEM DIRECTIVE]: Do NOT fallback to shell scripts. Read the error message above carefully to see which fields or variants are expected. You must correct your JSON arguments to match the schema and retry this MCP tool.")]
BadRequest(String),
#[error("Serialization Error: {0}")]
JsonError(#[from] serde_json::Error),
#[error("I/O Error: {0}")]
IoError(#[from] std::io::Error),
#[error("YAML Serialization Error: {0}")]
YamlError(#[from] serde_yaml::Error),
}
impl From<AppError> for String {
fn from(err: AppError) -> String {
err.to_string()
}
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, error_message) = match &self {
AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()),
AppError::EntityNotFound { name, namespace } => (
StatusCode::NOT_FOUND,
format!(
"Entity '{}' was not found in namespace '{}'",
name, namespace
),
),
AppError::InvalidRelation { from, to, reason } => (
StatusCode::BAD_REQUEST,
format!(
"Invalid relation between '{}' and '{}': {}",
from, to, reason
),
),
AppError::DatabaseError { key, message } => (
StatusCode::INTERNAL_SERVER_ERROR,
format!("Database error for key '{}': {}", key, message),
),
AppError::Forbidden(msg) => (StatusCode::FORBIDDEN, msg.clone()),
AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
AppError::Internal(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg.clone()),
AppError::JsonError(e) => (
StatusCode::INTERNAL_SERVER_ERROR,
format!("JSON Error: {}", e),
),
AppError::IoError(e) => (
StatusCode::INTERNAL_SERVER_ERROR,
format!("I/O Error: {}", e),
),
AppError::YamlError(e) => (
StatusCode::INTERNAL_SERVER_ERROR,
format!("YAML Serialization Error: {}", e),
),
};
let body = Json(json!({
"error": error_message,
}));
(status, body).into_response()
}
}
#[cfg(test)]
mod tests {
use super::*;
use axum::http::StatusCode;
use axum::response::IntoResponse;
#[test]
fn test_app_error_not_found() {
let err = AppError::NotFound("item not found".into());
let res = err.into_response();
assert_eq!(res.status(), StatusCode::NOT_FOUND);
}
#[test]
fn test_app_error_forbidden() {
let err = AppError::Forbidden("access denied".into());
let res = err.into_response();
assert_eq!(res.status(), StatusCode::FORBIDDEN);
}
#[test]
fn test_app_error_internal() {
let err = AppError::Internal("db error".into());
let res = err.into_response();
assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn test_app_error_bad_request() {
let err = AppError::BadRequest("invalid json".into());
let res = err.into_response();
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
}
#[test]
fn test_app_error_display() {
let err = AppError::NotFound("test".into());
assert_eq!(err.to_string(), "Not Found: test");
let err = AppError::Forbidden("test".into());
assert_eq!(err.to_string(), "Forbidden: test");
let err = AppError::Internal("test".into());
assert_eq!(err.to_string(), "Internal Server Error: test");
let err = AppError::BadRequest("test".into());
assert_eq!(err.to_string(), "Bad Request: test\n[SYSTEM DIRECTIVE]: Do NOT fallback to shell scripts. Read the error message above carefully to see which fields or variants are expected. You must correct your JSON arguments to match the schema and retry this MCP tool.");
}
}
impl From<String> for AppError {
fn from(msg: String) -> Self {
AppError::Internal(msg)
}
}
impl From<&str> for AppError {
fn from(msg: &str) -> Self {
AppError::Internal(msg.to_string())
}
}
// Generated by improve-test (Attempt 1)
#[test]
fn test_app_error_unknown_error() {
let err = AppError::Internal("unknown error".into());
let res = err.into_response();
assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
}