Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

## [0.14.1] - 2025-09-24
## [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.

### Fixed
- Removed the unused `BacktraceSlot::get` helper to restore builds with `-D warnings`.
Expand Down
47 changes: 35 additions & 12 deletions src/app_error/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}
};

Expand Down Expand Up @@ -65,9 +66,9 @@ impl Default for BacktraceSlot {
#[cfg(not(feature = "backtrace"))]
type BacktraceSlot = Option<Backtrace>;

/// 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.
Expand All @@ -87,6 +88,26 @@ pub struct Error {
telemetry_dirty: AtomicBool
}

/// Rich application error preserving domain code, taxonomy and metadata.
#[derive(Debug)]
pub struct Error {
inner: Box<ErrorInner>
}

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)
Expand Down Expand Up @@ -129,16 +150,18 @@ pub type AppResult<T, E = Error> = Result<T, E>;
impl Error {
pub(crate) fn new_raw(kind: AppErrorKind, message: Option<Cow<'static, str>>) -> 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)
})
}
}

Expand Down
2 changes: 0 additions & 2 deletions src/app_error/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,8 +427,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<u8> {
Ok(1)
}
Expand Down
2 changes: 0 additions & 2 deletions src/response/details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(self, payload: T) -> AppResult<Self>
where
T: Serialize
Expand Down
16 changes: 6 additions & 10 deletions src/response/mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,14 @@ impl Display for ErrorResponse {
}

impl From<AppError> 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()
};
Expand Down
Loading