From 878baec003b105184203022f2ad3bcb3d241d25c Mon Sep 17 00:00:00 2001 From: RA <70325462+RAprogramm@users.noreply.github.com> Date: Thu, 11 Sep 2025 13:13:35 +0700 Subject: [PATCH 1/2] Add status code helper --- Cargo.lock | 1 + Cargo.toml | 1 + src/response.rs | 43 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5f6bff5..f85bf23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1224,6 +1224,7 @@ dependencies = [ "actix-web", "axum", "config", + "http 0.2.12", "redis", "reqwest", "serde", diff --git a/Cargo.toml b/Cargo.toml index afa66cd..45606c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ tracing = "0.1" serde = { version = "1", features = ["derive"] } serde_json = { version = "1", optional = true } +http = "0.2" # опциональные интеграции axum = { version = "0.8", optional = true, default-features = false, features = [ diff --git a/src/response.rs b/src/response.rs index f1bae48..0e333c8 100644 --- a/src/response.rs +++ b/src/response.rs @@ -55,6 +55,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; +use http::StatusCode; use serde::{Deserialize, Serialize}; #[cfg(feature = "serde_json")] use serde_json::Value as JsonValue; @@ -162,6 +163,24 @@ impl ErrorResponse { self.www_authenticate = Some(value.into()); self } + + /// Convert numeric [`status`](ErrorResponse::status) into [`StatusCode`]. + /// + /// Invalid codes default to `StatusCode::INTERNAL_SERVER_ERROR`. + /// + /// # Examples + /// + /// ``` + /// use http::StatusCode; + /// use masterror::{AppCode, ErrorResponse}; + /// + /// let resp = ErrorResponse::new(404, AppCode::NotFound, "missing"); + /// assert_eq!(resp.status_code(), StatusCode::NOT_FOUND); + /// ``` + #[must_use] + pub fn status_code(&self) -> StatusCode { + StatusCode::from_u16(self.status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR) + } } /// Legacy constructor retained for migration purposes. @@ -221,7 +240,7 @@ mod axum_impl { use axum::{ Json, http::{ - HeaderValue, StatusCode, + HeaderValue, header::{RETRY_AFTER, WWW_AUTHENTICATE} }, response::{IntoResponse, Response} @@ -232,8 +251,7 @@ mod axum_impl { impl IntoResponse for ErrorResponse { fn into_response(self) -> Response { - let status = - StatusCode::from_u16(self.status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR); + let status = self.status_code(); // Serialize JSON body first (borrow self for payload). let mut response = (status, Json(&self)).into_response(); @@ -279,10 +297,7 @@ mod actix_impl { use actix_web::{ HttpRequest, HttpResponse, Responder, body::BoxBody, - http::{ - StatusCode, - header::{RETRY_AFTER, WWW_AUTHENTICATE} - } + http::header::{RETRY_AFTER, WWW_AUTHENTICATE} }; use super::ErrorResponse; @@ -291,8 +306,7 @@ mod actix_impl { type Body = BoxBody; fn respond_to(self, _req: &HttpRequest) -> HttpResponse { - let status = - StatusCode::from_u16(self.status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR); + let status = self.status_code(); let mut builder = HttpResponse::build(status); if let Some(retry) = self.retry { @@ -334,6 +348,17 @@ mod tests { assert_eq!(e.www_authenticate.as_deref(), Some(r#"Bearer realm="api""#)); } + #[test] + fn status_code_maps_invalid_to_internal_server_error() { + use http::StatusCode; + + let valid = ErrorResponse::new(404, AppCode::NotFound, "missing"); + assert_eq!(valid.status_code(), StatusCode::NOT_FOUND); + + let invalid = ErrorResponse::new(1000, AppCode::Internal, "oops"); + assert_eq!(invalid.status_code(), StatusCode::INTERNAL_SERVER_ERROR); + } + // --- Details: JSON vs text ---------------------------------------------- #[cfg(feature = "serde_json")] From 2b58d8360044059ceb1065b5649351c0b1bfed89 Mon Sep 17 00:00:00 2001 From: RA <70325462+RAprogramm@users.noreply.github.com> Date: Thu, 11 Sep 2025 13:26:50 +0700 Subject: [PATCH 2/2] Fix status code conversions --- src/response.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/response.rs b/src/response.rs index 806aafb..7f82b6c 100644 --- a/src/response.rs +++ b/src/response.rs @@ -184,7 +184,7 @@ impl ErrorResponse { /// use http::StatusCode; /// use masterror::{AppCode, ErrorResponse}; /// - /// let resp = ErrorResponse::new(404, AppCode::NotFound, "missing"); + /// let resp = ErrorResponse::new(404, AppCode::NotFound, "missing").expect("status"); /// assert_eq!(resp.status_code(), StatusCode::NOT_FOUND); /// ``` #[must_use] @@ -315,7 +315,10 @@ mod actix_impl { use actix_web::{ HttpRequest, HttpResponse, Responder, body::BoxBody, - http::header::{RETRY_AFTER, WWW_AUTHENTICATE} + http::{ + StatusCode as ActixStatus, + header::{RETRY_AFTER, WWW_AUTHENTICATE} + } }; use super::ErrorResponse; @@ -325,6 +328,8 @@ mod actix_impl { fn respond_to(self, _req: &HttpRequest) -> HttpResponse { let status = self.status_code(); + let status = ActixStatus::from_u16(status.as_u16()) + .unwrap_or(ActixStatus::INTERNAL_SERVER_ERROR); let mut builder = HttpResponse::build(status); if let Some(retry) = self.retry { @@ -377,10 +382,17 @@ mod tests { fn status_code_maps_invalid_to_internal_server_error() { use http::StatusCode; - let valid = ErrorResponse::new(404, AppCode::NotFound, "missing"); + let valid = ErrorResponse::new(404, AppCode::NotFound, "missing").expect("status"); assert_eq!(valid.status_code(), StatusCode::NOT_FOUND); - let invalid = ErrorResponse::new(1000, AppCode::Internal, "oops"); + let invalid = ErrorResponse { + status: 1000, + code: AppCode::Internal, + message: "oops".into(), + details: None, + retry: None, + www_authenticate: None + }; assert_eq!(invalid.status_code(), StatusCode::INTERNAL_SERVER_ERROR); }