137 lines
3.7 KiB
Rust
137 lines
3.7 KiB
Rust
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<String>) -> Self {
|
|
Self {
|
|
message: message.into(),
|
|
status: StatusCode::INTERNAL_SERVER_ERROR,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn bad_request(message: impl Into<String>) -> Self {
|
|
Self {
|
|
message: message.into(),
|
|
status: StatusCode::BAD_REQUEST,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn not_found(message: impl Into<String>) -> Self {
|
|
Self {
|
|
message: message.into(),
|
|
status: StatusCode::NOT_FOUND,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn unprocessable(message: impl Into<String>) -> 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<anyhow::Error> for ErrorResponse {
|
|
fn from(err: anyhow::Error) -> Self {
|
|
Self::internal(err.to_string())
|
|
}
|
|
}
|
|
|
|
impl From<ConfigError> 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<ModelConfigError> for ErrorResponse {
|
|
fn from(err: ModelConfigError) -> Self {
|
|
Self::internal(format!("Model configuration error: {}", err))
|
|
}
|
|
}
|
|
|
|
impl From<StatusCode> for ErrorResponse {
|
|
fn from(status: StatusCode) -> Self {
|
|
let message = status.canonical_reason().unwrap_or("Unknown error");
|
|
Self {
|
|
message: message.to_string(),
|
|
status,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<std::io::Error> for ErrorResponse {
|
|
fn from(err: std::io::Error) -> Self {
|
|
Self::internal(format!("IO error: {}", err))
|
|
}
|
|
}
|
|
|
|
impl From<serde_json::Error> for ErrorResponse {
|
|
fn from(err: serde_json::Error) -> Self {
|
|
Self::internal(format!("JSON serialization error: {}", err))
|
|
}
|
|
}
|
|
|
|
impl From<serde_yaml::Error> for ErrorResponse {
|
|
fn from(err: serde_yaml::Error) -> Self {
|
|
Self::unprocessable(format!("YAML parsing error: {}", err))
|
|
}
|
|
}
|
|
|
|
impl From<ProviderError> 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 }
|
|
}
|
|
}
|