From f2932b9133474c8269cdc020bc01a78a7716276a Mon Sep 17 00:00:00 2001 From: tv Date: Sat, 31 Oct 2026 04:51:52 +0100 Subject: api: unify error responses --- src/api/mod.rs | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) (limited to 'src/api/mod.rs') diff --git a/src/api/mod.rs b/src/api/mod.rs index 093ca6a..021bc52 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -169,14 +169,43 @@ async fn openapi_spec() -> impl IntoResponse { // --- shared helpers ---------------------------------------------------------- -pub(crate) fn store_status(error: StoreError, context: &str) -> StatusCode { - match error { - StoreError::Conflict => StatusCode::CONFLICT, +/// An error answer: its status, and why where it is known (`error_response`); a bare +/// status otherwise (`json_errors` gives it the body). +pub(crate) struct ApiError(StatusCode, Option); + +impl ApiError { + pub(crate) fn status(&self) -> StatusCode { + self.0 + } +} + +impl From for ApiError { + fn from(status: StatusCode) -> Self { + Self(status, None) + } +} + +impl IntoResponse for ApiError { + fn into_response(self) -> Response { + match self.1 { + Some(message) => error_response(self.0, message, None), + None => self.0.into_response(), + } + } +} + +/// A store error as an answer, saying why (`StoreError`'s text) where it is the +/// client's; others are logged with `context`, and are a bare `500`. +pub(crate) fn store_status(error: StoreError, context: &str) -> ApiError { + let status = match error { + StoreError::Conflict | StoreError::NotAList | StoreError::PastTheEnd { .. } => { + StatusCode::CONFLICT + } StoreError::NotFound => StatusCode::NOT_FOUND, - StoreError::NotAList | StoreError::PastTheEnd { .. } => StatusCode::CONFLICT, other => { error!(error = %format!("{other:#}"), "{context}"); - StatusCode::INTERNAL_SERVER_ERROR + return StatusCode::INTERNAL_SERVER_ERROR.into(); } - } + }; + ApiError(status, Some(error.to_string())) } -- cgit v1.3.1