use axum::{ http::StatusCode, response::{IntoResponse, Response}, Json, }; use goose::config::ConfigError; use goose::model::ConfigError as ModelConfigError; use goose_providers::errors::ProviderError; use serde::Serialize; use utoipa::ToSchema; #[derive(Debug, Serialize, ToSchema)] pub struct ErrorResponse { pub message: String, #[serde(skip)] pub status: StatusCode, } impl ErrorResponse { pub(crate) fn internal(message: impl Into) -> Self { Self { message: message.into(), status: StatusCode::INTERNAL_SERVER_ERROR, } } pub(crate) fn bad_request(message: impl Into) -> Self { Self { message: message.into(), status: StatusCode::BAD_REQUEST, } } pub(crate) fn not_found(message: impl Into) -> Self { Self { message: message.into(), status: StatusCode::NOT_FOUND, } } pub(crate) fn unprocessable(message: impl Into) -> Self { Self { message: message.into(), status: StatusCode::UNPROCESSABLE_ENTITY, } } } impl IntoResponse for ErrorResponse { fn into_response(self) -> Response { if self.status.is_server_error() { tracing::error!(status = %self.status, message = %self.message, "server error response"); } else if self.status.is_client_error() { tracing::warn!(status = %self.status, message = %self.message, "client error response"); } let body = Json(serde_json::json!({ "message": self.message, })); (self.status, body).into_response() } } impl From for ErrorResponse { fn from(err: anyhow::Error) -> Self { Self::internal(err.to_string()) } } impl From for ErrorResponse { fn from(err: ConfigError) -> Self { match err { ConfigError::NotFound(key) => Self::not_found(format!("Config key not found: {}", key)), _ => Self::internal(err.to_string()), } } } impl From for ErrorResponse { fn from(err: ModelConfigError) -> Self { Self::internal(format!("Model configuration error: {}", err)) } } impl From for ErrorResponse { fn from(status: StatusCode) -> Self { let message = status.canonical_reason().unwrap_or("Unknown error"); Self { message: message.to_string(), status, } } } impl From for ErrorResponse { fn from(err: std::io::Error) -> Self { Self::internal(format!("IO error: {}", err)) } } impl From for ErrorResponse { fn from(err: serde_json::Error) -> Self { Self::internal(format!("JSON serialization error: {}", err)) } } impl From for ErrorResponse { fn from(err: serde_yaml::Error) -> Self { Self::unprocessable(format!("YAML parsing error: {}", err)) } } impl From for ErrorResponse { fn from(err: ProviderError) -> Self { let (status, message) = match err { ProviderError::Authentication(_) => ( StatusCode::BAD_REQUEST, format!("Authentication failed: {}", err), ), ProviderError::UsageError(_) => { (StatusCode::BAD_REQUEST, format!("Usage error: {}", err)) } ProviderError::RateLimitExceeded { .. } => ( StatusCode::TOO_MANY_REQUESTS, format!("Rate limit exceeded: {}", err), ), _ => ( StatusCode::INTERNAL_SERVER_ERROR, format!("Provider error: {}", err), ), }; Self { message, status } } }