use std::fmt; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; /// Registry error types #[derive(Debug, thiserror::Error)] pub enum RegistryError { #[error("Extension not found: {0}")] NotFound(String), #[error("Invalid extension type: {0}")] InvalidType(String), #[error("Invalid version format: {0}")] InvalidVersion(String), #[error("Gitea API error: {0}")] Gitea(String), #[error("Forgejo API error: {0}")] Forgejo(String), #[error("GitHub API error: {0}")] Github(String), #[error("OCI registry error: {0}")] Oci(String), #[error("Cache error: {0}")] Cache(String), #[error("HTTP request error: {0}")] Http(#[from] reqwest::Error), #[error("IO error: {0}")] Io(#[from] std::io::Error), #[error("JSON serialization error: {0}")] Json(#[from] serde_json::Error), #[error("Configuration error: {0}")] Config(String), #[error("Authentication error: {0}")] Auth(String), #[error("Rate limit exceeded")] RateLimit, #[error("No backends configured")] NoBackendsConfigured, #[error("All backends failed: {0}")] MultiBackendFailure(String), #[error("Internal server error: {0}")] Internal(String), } /// Result type alias pub type Result = std::result::Result; /// API error response #[derive(Debug, serde::Serialize)] pub struct ApiErrorResponse { pub error: String, pub message: String, #[serde(skip_serializing_if = "Option::is_none")] pub details: Option, } impl IntoResponse for RegistryError { fn into_response(self) -> Response { let (status, error_type) = match &self { RegistryError::NotFound(_) => (StatusCode::NOT_FOUND, "not_found"), RegistryError::InvalidType(_) => (StatusCode::BAD_REQUEST, "invalid_type"), RegistryError::InvalidVersion(_) => (StatusCode::BAD_REQUEST, "invalid_version"), RegistryError::Auth(_) => (StatusCode::UNAUTHORIZED, "auth_error"), RegistryError::RateLimit => (StatusCode::TOO_MANY_REQUESTS, "rate_limit"), RegistryError::Config(_) | RegistryError::NoBackendsConfigured => { (StatusCode::INTERNAL_SERVER_ERROR, "config_error") } RegistryError::MultiBackendFailure(_) => { (StatusCode::SERVICE_UNAVAILABLE, "backend_failure") } _ => (StatusCode::INTERNAL_SERVER_ERROR, "internal_error"), }; let body = ApiErrorResponse { error: error_type.to_string(), message: self.to_string(), details: None, }; (status, axum::Json(body)).into_response() } } impl fmt::Display for ApiErrorResponse { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}: {}", self.error, self.message) } }