use std::fmt; pub use export::is_unique_violation; #[derive(Debug)] pub enum SpaceError { BadRequest(String), Unauthorized, Forbidden, JoinNotAllowed(String), JoinRequirementNotMet(String), JoinRequestCooldown(String), NotFound, Conflict(String), RateLimited(String), Internal(String), } impl fmt::Display for SpaceError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::BadRequest(message) => write!(f, "{message}"), Self::Unauthorized => write!(f, "unauthorized"), Self::Forbidden => write!(f, "forbidden"), Self::JoinNotAllowed(message) => write!(f, "{message}"), Self::JoinRequirementNotMet(message) => write!(f, "{message}"), Self::JoinRequestCooldown(message) => write!(f, "{message}"), Self::NotFound => write!(f, "not found"), Self::Conflict(message) => write!(f, "{message}"), Self::RateLimited(message) => write!(f, "{message}"), Self::Internal(message) => write!(f, "{message}"), } } } impl std::error::Error for SpaceError {} impl From for SpaceError { fn from(error: deletion::DeletionError) -> Self { use deletion::DeletionError; match error { DeletionError::Database(error) => Self::from(error), DeletionError::Conflict(message) => Self::Conflict(message), DeletionError::NotFound(message) => { tracing::debug!(%message, "deletion target not found"); Self::NotFound } DeletionError::KenAuth(message) | DeletionError::KenShell(message) | DeletionError::Config(message) => Self::Internal(message), } } } impl From for SpaceError { fn from(error: auth::client::error::KenAuthError) -> Self { match error { auth::client::error::KenAuthError::NotFound => Self::NotFound, auth::client::error::KenAuthError::Unauthenticated => Self::Unauthorized, auth::client::error::KenAuthError::Unavailable(message) => { Self::Internal(format!("kenoauth unavailable: {message}")) } auth::client::error::KenAuthError::Rejected(message) => { let lower = message.to_lowercase(); if lower.contains("already") || lower.contains("taken") { Self::Conflict(message) } else if lower.contains("forbidden") || lower.contains("permission") { Self::Forbidden } else { Self::BadRequest(message) } } auth::client::error::KenAuthError::Config(message) | auth::client::error::KenAuthError::Internal(message) => Self::Internal(message), } } } export::db_error_conversions!(SpaceError); export::storage_error_conversion!(SpaceError); export::export_error_conversion!(SpaceError); export::json_payload_conversion!(SpaceError); export::api_error! { log: "space", pub enum ApiError { BadRequest(400, "request failed validation"), Unauthorized(401, "authentication required"), Forbidden(403, "operation not allowed"), NotFound(404, "resource not found"), Conflict(409, "resource conflict"), TooManyRequests(429, "rate limit exceeded"), Internal(500, "internal server error"), } } impl From for ApiError { fn from(error: SpaceError) -> Self { match error { SpaceError::BadRequest(msg) => Self::bad_request(msg), SpaceError::Unauthorized => Self::unauthorized(), SpaceError::Forbidden => Self::forbidden(), SpaceError::JoinNotAllowed(msg) => Self::forbidden_with_code("join_not_allowed", msg), SpaceError::JoinRequirementNotMet(msg) => { Self::forbidden_with_code("join_requirement_not_met", msg) } SpaceError::JoinRequestCooldown(msg) => { Self::forbidden_with_code("join_request_cooldown", msg) } SpaceError::NotFound => Self::not_found("space resource not found"), SpaceError::Conflict(msg) => Self::conflict(msg), SpaceError::RateLimited(msg) => Self::rate_limited(msg), SpaceError::Internal(message) => Self::internal(message), } } }