From 023697c48d5e5a4d312d613fced67d27c244d49a Mon Sep 17 00:00:00 2001 From: RA <70325462+RAprogramm@users.noreply.github.com> Date: Tue, 23 Sep 2025 13:05:56 +0700 Subject: [PATCH] Refactor AppError storage and update lint configuration --- CHANGELOG.md | 11 ++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 14 ++++++------ src/app_error/core.rs | 47 ++++++++++++++++++++++++++++++----------- src/app_error/tests.rs | 2 -- src/response/details.rs | 2 -- src/response/mapping.rs | 16 ++++++-------- 8 files changed, 61 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2526e2..18754c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [0.14.1] - 2025-09-25 + +### Changed +- Boxed the internal `AppError` payload inside a new `ErrorInner` allocation, + keeping public field access via `Deref` while shrinking the error to a + pointer-sized handle that shares metadata, retry hints, and backtrace state. + +### Removed +- Dropped `clippy::result_large_err` allowances in response helpers and tests + now that `AppError` is pointer-sized and lint-clean without suppressions. + ## [0.14.0] - 2025-09-24 ### Added diff --git a/Cargo.lock b/Cargo.lock index 0400bbe..41638a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1624,7 +1624,7 @@ dependencies = [ [[package]] name = "masterror" -version = "0.14.0" +version = "0.14.1" dependencies = [ "actix-web", "axum", diff --git a/Cargo.toml b/Cargo.toml index 0bfdc16..68dbcc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "masterror" -version = "0.14.0" +version = "0.14.1" rust-version = "1.90" edition = "2024" license = "MIT OR Apache-2.0" diff --git a/README.md b/README.md index 3b8adc6..45823d1 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,9 @@ guides, comparisons with `thiserror`/`anyhow`, and troubleshooting recipes. ~~~toml [dependencies] -masterror = { version = "0.14.0", default-features = false } +masterror = { version = "0.14.1", default-features = false } # or with features: -# masterror = { version = "0.14.0", features = [ +# masterror = { version = "0.14.1", features = [ # "axum", "actix", "openapi", "serde_json", # "tracing", "metrics", "backtrace", "sqlx", # "sqlx-migrate", "reqwest", "redis", "validator", @@ -77,10 +77,10 @@ masterror = { version = "0.14.0", default-features = false } ~~~toml [dependencies] # lean core -masterror = { version = "0.14.0", default-features = false } +masterror = { version = "0.14.1", default-features = false } # with Axum/Actix + JSON + integrations -# masterror = { version = "0.14.0", features = [ +# masterror = { version = "0.14.1", features = [ # "axum", "actix", "openapi", "serde_json", # "tracing", "metrics", "backtrace", "sqlx", # "sqlx-migrate", "reqwest", "redis", "validator", @@ -714,13 +714,13 @@ assert_eq!(resp.status, 401); Minimal core: ~~~toml -masterror = { version = "0.14.0", default-features = false } +masterror = { version = "0.14.1", default-features = false } ~~~ API (Axum + JSON + deps): ~~~toml -masterror = { version = "0.14.0", features = [ +masterror = { version = "0.14.1", features = [ "axum", "serde_json", "openapi", "sqlx", "reqwest", "redis", "validator", "config", "tokio" ] } @@ -729,7 +729,7 @@ masterror = { version = "0.14.0", features = [ API (Actix + JSON + deps): ~~~toml -masterror = { version = "0.14.0", features = [ +masterror = { version = "0.14.1", features = [ "actix", "serde_json", "openapi", "sqlx", "reqwest", "redis", "validator", "config", "tokio" ] } diff --git a/src/app_error/core.rs b/src/app_error/core.rs index 3625383..6d9711f 100644 --- a/src/app_error/core.rs +++ b/src/app_error/core.rs @@ -5,6 +5,7 @@ use std::{ borrow::Cow, error::Error as StdError, fmt::{Display, Formatter, Result as FmtResult}, + ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, Ordering} }; @@ -69,9 +70,9 @@ impl Default for BacktraceSlot { #[cfg(not(feature = "backtrace"))] type BacktraceSlot = Option; -/// Rich application error preserving domain code, taxonomy and metadata. #[derive(Debug)] -pub struct Error { +#[doc(hidden)] +pub struct ErrorInner { /// Stable machine-readable error code. pub code: AppCode, /// Semantic error category. @@ -91,6 +92,26 @@ pub struct Error { telemetry_dirty: AtomicBool } +/// Rich application error preserving domain code, taxonomy and metadata. +#[derive(Debug)] +pub struct Error { + inner: Box +} + +impl Deref for Error { + type Target = ErrorInner; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for Error { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { Display::fmt(&self.kind, f) @@ -133,16 +154,18 @@ pub type AppResult = Result; impl Error { pub(crate) fn new_raw(kind: AppErrorKind, message: Option>) -> Self { Self { - code: AppCode::from(kind), - kind, - message, - metadata: Metadata::new(), - edit_policy: MessageEditPolicy::Preserve, - retry: None, - www_authenticate: None, - source: None, - backtrace: BacktraceSlot::default(), - telemetry_dirty: AtomicBool::new(true) + inner: Box::new(ErrorInner { + code: AppCode::from(kind), + kind, + message, + metadata: Metadata::new(), + edit_policy: MessageEditPolicy::Preserve, + retry: None, + www_authenticate: None, + source: None, + backtrace: BacktraceSlot::default(), + telemetry_dirty: AtomicBool::new(true) + }) } } diff --git a/src/app_error/tests.rs b/src/app_error/tests.rs index df607ca..749d4fe 100644 --- a/src/app_error/tests.rs +++ b/src/app_error/tests.rs @@ -412,8 +412,6 @@ fn metrics_counter_is_incremented_once() { #[test] fn result_alias_is_generic() { - // The alias intentionally preserves the full AppError payload size. - #[allow(clippy::result_large_err)] fn app() -> super::AppResult { Ok(1) } diff --git a/src/response/details.rs b/src/response/details.rs index 92472ca..ceca799 100644 --- a/src/response/details.rs +++ b/src/response/details.rs @@ -54,8 +54,6 @@ impl ErrorResponse { /// assert!(resp.details.is_some()); /// # } /// ``` - // AppError carries telemetry metadata; keep the rich payload despite the lint. - #[allow(clippy::result_large_err)] pub fn with_details(self, payload: T) -> AppResult where T: Serialize diff --git a/src/response/mapping.rs b/src/response/mapping.rs index e907581..cc522c5 100644 --- a/src/response/mapping.rs +++ b/src/response/mapping.rs @@ -11,18 +11,14 @@ impl Display for ErrorResponse { } impl From for ErrorResponse { - fn from(err: AppError) -> Self { - let AppError { - code, - kind, - message, - retry, - www_authenticate, - .. - } = err; + fn from(mut err: AppError) -> Self { + let kind = err.kind; + let code = err.code; + let retry = err.retry.take(); + let www_authenticate = err.www_authenticate.take(); let status = kind.http_status(); - let message = match message { + let message = match err.message.take() { Some(msg) => msg.into_owned(), None => kind.to_string() };