From 12bd6777a2ba94f57fddca053130919bdfa27d80 Mon Sep 17 00:00:00 2001 From: RA <70325462+RAprogramm@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:06:53 +0700 Subject: [PATCH] Add AppError detail payload handling --- CHANGELOG.md | 13 +++ Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 4 +- docs/wiki/masterror-application-guide.md | 14 ++- docs/wiki/patterns-and-troubleshooting.md | 14 ++- src/app_error/core.rs | 97 ++++++++++++++++++ src/app_error/tests.rs | 39 ++++++++ src/response/internal.rs | 1 + src/response/mapping.rs | 28 +++++- src/response/problem_json.rs | 52 ++++++++++ src/response/tests.rs | 98 +++++++++++++++++++ .../fail/enum_missing_variant.stderr | 7 +- tests/ui/app_error/fail/missing_code.stderr | 4 +- tests/ui/app_error/fail/missing_kind.stderr | 2 +- tests/ui/formatter/fail/duplicate_fmt.stderr | 2 +- .../fail/implicit_after_named.stderr | 1 - .../ui/formatter/fail/unsupported_flag.stderr | 4 +- .../fail/unsupported_formatter.stderr | 4 +- .../ui/formatter/fail/uppercase_binary.stderr | 4 +- .../formatter/fail/uppercase_pointer.stderr | 4 +- tests/ui/masterror/fail/duplicate_attr.stderr | 2 +- .../masterror/fail/duplicate_telemetry.stderr | 2 +- tests/ui/masterror/fail/empty_redact.stderr | 2 +- .../fail/enum_missing_variant.stderr | 7 +- .../ui/masterror/fail/missing_category.stderr | 4 +- tests/ui/masterror/fail/missing_code.stderr | 4 +- tests/ui/masterror/fail/unknown_option.stderr | 2 +- 28 files changed, 371 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74cbea5..6da1abf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [0.23.0] - 2025-10-12 + +### Added +- Added feature-gated detail payload storage to `AppError` with new + `with_details`, `with_details_json`, and `with_details_text` helpers plus unit + tests covering both serde-json configurations. +- Exposed the stored details through `ProblemJson` and legacy `ErrorResponse` + conversions so RFC7807 and historical payloads emit the supplied data. + +### Changed +- Updated the documentation set to highlight the new helpers and clarify + feature requirements for attaching structured details. + ## [0.22.0] - 2025-10-11 ### Added diff --git a/Cargo.lock b/Cargo.lock index 6b697e7..1718ae4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1727,7 +1727,7 @@ dependencies = [ [[package]] name = "masterror" -version = "0.22.0" +version = "0.23.0" dependencies = [ "actix-web", "axum 0.8.4", diff --git a/Cargo.toml b/Cargo.toml index 794a1be..d335367 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "masterror" -version = "0.22.0" +version = "0.23.0" rust-version = "1.90" edition = "2024" license = "MIT OR Apache-2.0" diff --git a/README.md b/README.md index 5829698..fa67970 100644 --- a/README.md +++ b/README.md @@ -74,9 +74,9 @@ The build script keeps the full feature snippet below in sync with ~~~toml [dependencies] -masterror = { version = "0.22.0", default-features = false } +masterror = { version = "0.23.0", default-features = false } # or with features: -# masterror = { version = "0.22.0", features = [ +# masterror = { version = "0.23.0", features = [ # "std", "axum", "actix", "openapi", # "serde_json", "tracing", "metrics", "backtrace", # "sqlx", "sqlx-migrate", "reqwest", "redis", diff --git a/docs/wiki/masterror-application-guide.md b/docs/wiki/masterror-application-guide.md index d936189..af9d833 100644 --- a/docs/wiki/masterror-application-guide.md +++ b/docs/wiki/masterror-application-guide.md @@ -67,7 +67,9 @@ pub fn parse_payload(json: &str) -> masterror::AppResult<&str> { ``` `with_context` stores the original `serde_json::Error` for logging; clients only -see the sanitized message, code, and JSON details. +see the sanitized message, code, and JSON details. Enable the `serde_json` +feature to use `.with_details(..)`; without it, fall back to +`AppError::with_details_text` for plain-text payloads. ## Deriving domain errors @@ -154,10 +156,12 @@ fn missing_field_is_bad_request() { assert!(matches!(err.kind, AppErrorKind::BadRequest)); assert_eq!(err.code.unwrap().as_str(), "MISSING_FIELD"); - let response: masterror::ErrorResponse = err.clone().into(); - assert_eq!(response.status.as_u16(), 400); + let response: masterror::ErrorResponse = (&err).into(); + assert_eq!(response.status, 400); + assert!(response.details.is_some()); } ``` -Cloning is cheap because `AppError` stores data on the stack and shares context -via `Arc` under the hood. Use these assertions to guarantee stable APIs. +Use these assertions to guarantee stable APIs without exposing secrets. Borrowed +conversions (`(&err).into()`) preserve the original error so it can be reused in +additional assertions. diff --git a/docs/wiki/patterns-and-troubleshooting.md b/docs/wiki/patterns-and-troubleshooting.md index 87a2249..2c62db2 100644 --- a/docs/wiki/patterns-and-troubleshooting.md +++ b/docs/wiki/patterns-and-troubleshooting.md @@ -56,7 +56,8 @@ pub fn validate(payload: &CreateUser) -> masterror::AppResult<()> { ``` `validator::ValidationErrors` implements `Serialize`, so it plugs directly into -`with_details`. +`with_details`. When `serde_json` is disabled, switch to +`AppError::with_details_text`. ## Emitting HTTP responses manually @@ -64,10 +65,10 @@ Sometimes you need to control the HTTP layer yourself (e.g., custom middleware). Convert `AppError` into `ErrorResponse` and format it however you need. ```rust -fn to_json(err: &masterror::AppError) -> serde_json::Value { - let response: masterror::ErrorResponse = err.clone().into(); +fn to_json(err: masterror::AppError) -> serde_json::Value { + let response: masterror::ErrorResponse = err.into(); serde_json::json!({ - "status": response.status.as_u16(), + "status": response.status, "code": response.code, "message": response.message, "details": response.details, @@ -75,9 +76,6 @@ fn to_json(err: &masterror::AppError) -> serde_json::Value { } ``` -The clone is cheap because `AppError` uses shared references for heavy context -objects. - ## Capturing reproducible logs 1. Log errors at the boundary with `tracing::error!`, including `kind`, @@ -109,7 +107,7 @@ reconstruct what happened. | Validation failures return HTTP 500 | Enable the `validator` feature and expose handlers as `AppResult`. | | JSON response lacks `code` | Call `.with_code(AppCode::new("..."))` or derive it via `#[app_error(code = ...)]`. | | Logs show duplicated errors | Log once per request at the boundary; do not log again inside helpers. | -| `with_details` fails to compile | Ensure the value implements `Serialize` (derive or implement it manually). | +| `with_details` fails to compile | Ensure the value implements `Serialize` and enable the `serde_json` feature, or call `with_details_text`. | | Need to inspect nested errors | Call `err.context()` to retrieve captured sources, including `anyhow::Error`. | ## Testing strategies diff --git a/src/app_error/core.rs b/src/app_error/core.rs index a4b455f..613c2e8 100644 --- a/src/app_error/core.rs +++ b/src/app_error/core.rs @@ -20,6 +20,10 @@ use std::{ } }; +#[cfg(feature = "serde_json")] +use serde::Serialize; +#[cfg(feature = "serde_json")] +use serde_json::{Value as JsonValue, to_value}; #[cfg(feature = "tracing")] use tracing::{Level, event}; @@ -61,6 +65,12 @@ pub struct ErrorInner { pub retry: Option, /// Optional authentication challenge for `WWW-Authenticate`. pub www_authenticate: Option, + /// Optional structured details exposed to clients. + #[cfg(feature = "serde_json")] + pub details: Option, + /// Optional textual details when JSON is unavailable. + #[cfg(not(feature = "serde_json"))] + pub details: Option, pub source: Option>, #[cfg(feature = "backtrace")] pub backtrace: Option, @@ -238,6 +248,7 @@ impl Error { edit_policy: MessageEditPolicy::Preserve, retry: None, www_authenticate: None, + details: None, source: None, #[cfg(feature = "backtrace")] backtrace: None, @@ -461,6 +472,92 @@ impl Error { self } + /// Attach structured JSON details for the client payload. + /// + /// The details are omitted from responses when the error has been marked as + /// [`redactable`](Self::redactable). + /// + /// # Examples + /// + /// ```rust + /// # #[cfg(feature = "serde_json")] + /// # { + /// use masterror::{AppError, AppErrorKind}; + /// use serde_json::json; + /// + /// let err = AppError::new(AppErrorKind::Validation, "invalid input") + /// .with_details_json(json!({"field": "email"})); + /// assert!(err.details.is_some()); + /// # } + /// ``` + #[must_use] + #[cfg(feature = "serde_json")] + pub fn with_details_json(mut self, details: JsonValue) -> Self { + self.details = Some(details); + self.mark_dirty(); + self + } + + /// Serialize and attach structured details. + /// + /// Returns [`AppError`] with [`AppErrorKind::BadRequest`] if serialization + /// fails. + /// + /// # Examples + /// + /// ```rust + /// # #[cfg(feature = "serde_json")] + /// # { + /// use masterror::{AppError, AppErrorKind}; + /// use serde::Serialize; + /// + /// #[derive(Serialize)] + /// struct Extra { + /// reason: &'static str + /// } + /// + /// let err = AppError::new(AppErrorKind::BadRequest, "invalid") + /// .with_details(Extra { + /// reason: "missing" + /// }) + /// .expect("details should serialize"); + /// assert!(err.details.is_some()); + /// # } + /// ``` + #[cfg(feature = "serde_json")] + #[allow(clippy::result_large_err)] + pub fn with_details(self, payload: T) -> crate::AppResult + where + T: Serialize + { + let details = to_value(payload).map_err(|err| Self::bad_request(err.to_string()))?; + Ok(self.with_details_json(details)) + } + + /// Attach plain-text details for client payloads. + /// + /// The text is omitted from responses when the error is + /// [`redactable`](Self::redactable). + /// + /// # Examples + /// + /// ```rust + /// # #[cfg(not(feature = "serde_json"))] + /// # { + /// use masterror::{AppError, AppErrorKind}; + /// + /// let err = AppError::new(AppErrorKind::Internal, "boom").with_details_text("retry later"); + /// assert!(err.details.is_some()); + /// # } + /// ``` + #[must_use] + #[cfg(not(feature = "serde_json"))] + pub fn with_details_text(mut self, details: impl Into) -> Self { + self.details = Some(details.into()); + self.mark_dirty(); + self + } + /// Borrow the attached metadata. #[must_use] pub fn metadata(&self) -> &Metadata { diff --git a/src/app_error/tests.rs b/src/app_error/tests.rs index 592d911..942601a 100644 --- a/src/app_error/tests.rs +++ b/src/app_error/tests.rs @@ -173,6 +173,45 @@ fn metadata_and_code_are_preserved() { assert_eq!(metadata.get("attempt"), Some(&FieldValue::I64(2))); } +#[cfg(feature = "serde_json")] +#[test] +fn with_details_json_attaches_payload() { + use serde_json::json; + + let payload = json!({"field": "email"}); + let err = AppError::validation("invalid").with_details_json(payload.clone()); + assert_eq!(err.details, Some(payload)); +} + +#[cfg(feature = "serde_json")] +#[test] +fn with_details_serialization_failure_is_bad_request() { + use serde::{Serialize, Serializer}; + + struct Failing; + + impl Serialize for Failing { + fn serialize(&self, _: S) -> Result + where + S: Serializer + { + Err(serde::ser::Error::custom("nope")) + } + } + + let err = AppError::internal("boom") + .with_details(Failing) + .expect_err("should fail"); + assert!(matches!(err.kind, AppErrorKind::BadRequest)); +} + +#[cfg(not(feature = "serde_json"))] +#[test] +fn with_details_text_attaches_payload() { + let err = AppError::internal("boom").with_details_text("retry later"); + assert_eq!(err.details.as_deref(), Some("retry later")); +} + #[test] fn context_redact_field_overrides_policy() { let err = super::Context::new(AppErrorKind::Service) diff --git a/src/response/internal.rs b/src/response/internal.rs index cc30758..6f03b13 100644 --- a/src/response/internal.rs +++ b/src/response/internal.rs @@ -56,6 +56,7 @@ impl Debug for ProblemJsonFormatter<'_> { .field("title", &self.inner.title) .field("status", &self.inner.status) .field("detail", &self.inner.detail) + .field("details", &self.inner.details) .field("code", &self.inner.code) .field("grpc", &self.inner.grpc) .field("metadata", &self.inner.metadata) diff --git a/src/response/mapping.rs b/src/response/mapping.rs index ecd3f4d..fb4848f 100644 --- a/src/response/mapping.rs +++ b/src/response/mapping.rs @@ -24,12 +24,24 @@ impl From for ErrorResponse { Some(msg) if !matches!(policy, crate::MessageEditPolicy::Redact) => msg.into_owned(), _ => kind.to_string() }; + #[cfg(feature = "serde_json")] + let details = if matches!(policy, crate::MessageEditPolicy::Redact) { + None + } else { + err.details.take() + }; + #[cfg(not(feature = "serde_json"))] + let details = if matches!(policy, crate::MessageEditPolicy::Redact) { + None + } else { + err.details.take() + }; Self { status, code, message, - details: None, + details, retry, www_authenticate } @@ -44,12 +56,24 @@ impl From<&AppError> for ErrorResponse { } else { err.render_message().into_owned() }; + #[cfg(feature = "serde_json")] + let details = if matches!(err.edit_policy, crate::MessageEditPolicy::Redact) { + None + } else { + err.details.clone() + }; + #[cfg(not(feature = "serde_json"))] + let details = if matches!(err.edit_policy, crate::MessageEditPolicy::Redact) { + None + } else { + err.details.clone() + }; Self { status, code: err.code, message, - details: None, + details, retry: err.retry, www_authenticate: err.www_authenticate.clone() } diff --git a/src/response/problem_json.rs b/src/response/problem_json.rs index 9478f50..76e9411 100644 --- a/src/response/problem_json.rs +++ b/src/response/problem_json.rs @@ -117,6 +117,14 @@ pub struct ProblemJson { /// Optional human-readable detail (redacted when marked private). #[serde(skip_serializing_if = "Option::is_none")] pub detail: Option>, + /// Optional structured details emitted to clients. + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg(feature = "serde_json")] + pub details: Option, + /// Optional textual details emitted to clients when JSON is disabled. + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg(not(feature = "serde_json"))] + pub details: Option, /// Stable machine-readable code. pub code: AppCode, /// Optional gRPC mapping for multi-protocol clients. @@ -158,6 +166,7 @@ impl ProblemJson { let message = error.message.take(); let metadata = core::mem::take(&mut error.metadata); let edit_policy = error.edit_policy; + let details = sanitize_details_owned(error.details.take(), edit_policy); let retry = error.retry.take(); let www_authenticate = error.www_authenticate.take(); @@ -172,6 +181,7 @@ impl ProblemJson { title, status, detail, + details, code, grpc: Some(mapping.grpc()), metadata, @@ -201,6 +211,7 @@ impl ProblemJson { let status = error.kind.http_status(); let title = Cow::Owned(error.kind.to_string()); let detail = sanitize_detail_ref(error); + let details = sanitize_details_ref(error); let metadata = sanitize_metadata_ref(error.metadata(), error.edit_policy); Self { @@ -208,6 +219,7 @@ impl ProblemJson { title, status, detail, + details, code: error.code, grpc: Some(mapping.grpc()), metadata, @@ -244,6 +256,7 @@ impl ProblemJson { title: Cow::Owned(mapping.kind().to_string()), status: response.status, detail, + details: response.details, code: response.code, grpc: Some(mapping.grpc()), metadata: None, @@ -400,6 +413,45 @@ fn sanitize_detail_ref(error: &AppError) -> Option> { Some(Cow::Owned(error.render_message().into_owned())) } +#[cfg(feature = "serde_json")] +fn sanitize_details_owned( + details: Option, + policy: MessageEditPolicy +) -> Option { + if matches!(policy, MessageEditPolicy::Redact) { + None + } else { + details + } +} + +#[cfg(not(feature = "serde_json"))] +fn sanitize_details_owned(details: Option, policy: MessageEditPolicy) -> Option { + if matches!(policy, MessageEditPolicy::Redact) { + None + } else { + details + } +} + +#[cfg(feature = "serde_json")] +fn sanitize_details_ref(error: &AppError) -> Option { + if matches!(error.edit_policy, MessageEditPolicy::Redact) { + None + } else { + error.details.clone() + } +} + +#[cfg(not(feature = "serde_json"))] +fn sanitize_details_ref(error: &AppError) -> Option { + if matches!(error.edit_policy, MessageEditPolicy::Redact) { + None + } else { + error.details.clone() + } +} + fn sanitize_metadata_owned( metadata: Metadata, policy: MessageEditPolicy diff --git a/src/response/tests.rs b/src/response/tests.rs index d7f11b0..5eb8b74 100644 --- a/src/response/tests.rs +++ b/src/response/tests.rs @@ -126,6 +126,104 @@ fn details_text_are_attached() { assert_eq!(e.details.as_deref(), Some("retry later")); } +#[cfg(feature = "serde_json")] +#[test] +fn app_error_mappings_propagate_json_details() { + use serde_json::json; + + let payload = json!({"hint": "enable"}); + + let resp: ErrorResponse = AppError::validation("invalid") + .with_details_json(payload.clone()) + .into(); + assert_eq!(resp.details, Some(payload.clone())); + + let borrowed = AppError::validation("invalid").with_details_json(payload.clone()); + let resp_ref: ErrorResponse = (&borrowed).into(); + assert_eq!(resp_ref.details, Some(payload.clone())); + + let problem_owned = ProblemJson::from_app_error( + AppError::validation("invalid").with_details_json(payload.clone()) + ); + assert_eq!(problem_owned.details, Some(payload.clone())); + + let problem_ref = ProblemJson::from_ref(&borrowed); + assert_eq!(problem_ref.details, Some(payload)); +} + +#[cfg(feature = "serde_json")] +#[test] +fn redacted_app_error_strips_json_details() { + use serde_json::json; + + let resp: ErrorResponse = AppError::internal("boom") + .with_details_json(json!({"private": true})) + .redactable() + .into(); + assert!(resp.details.is_none()); + + let borrowed = AppError::internal("boom") + .with_details_json(json!({"private": true})) + .redactable(); + let resp_ref: ErrorResponse = (&borrowed).into(); + assert!(resp_ref.details.is_none()); + let problem = ProblemJson::from_ref(&borrowed); + assert!(problem.details.is_none()); + + let owned_problem = ProblemJson::from_app_error( + AppError::internal("boom") + .with_details_json(json!({"private": true})) + .redactable() + ); + assert!(owned_problem.details.is_none()); +} + +#[cfg(not(feature = "serde_json"))] +#[test] +fn app_error_mappings_propagate_text_details() { + let resp: ErrorResponse = AppError::validation("invalid") + .with_details_text("enable feature") + .into(); + assert_eq!(resp.details.as_deref(), Some("enable feature")); + + let borrowed = AppError::validation("invalid").with_details_text("enable feature"); + let resp_ref: ErrorResponse = (&borrowed).into(); + assert_eq!(resp_ref.details.as_deref(), Some("enable feature")); + + let problem_owned = ProblemJson::from_app_error( + AppError::validation("invalid").with_details_text("enable feature") + ); + assert_eq!(problem_owned.details.as_deref(), Some("enable feature")); + + let problem_ref = ProblemJson::from_ref(&borrowed); + assert_eq!(problem_ref.details.as_deref(), Some("enable feature")); +} + +#[cfg(not(feature = "serde_json"))] +#[test] +fn redacted_app_error_strips_text_details() { + let resp: ErrorResponse = AppError::internal("boom") + .with_details_text("private") + .redactable() + .into(); + assert!(resp.details.is_none()); + + let borrowed = AppError::internal("boom") + .with_details_text("private") + .redactable(); + let resp_ref: ErrorResponse = (&borrowed).into(); + assert!(resp_ref.details.is_none()); + let problem = ProblemJson::from_ref(&borrowed); + assert!(problem.details.is_none()); + + let owned_problem = ProblemJson::from_app_error( + AppError::internal("boom") + .with_details_text("private") + .redactable() + ); + assert!(owned_problem.details.is_none()); +} + // --- From<&AppError> mapping -------------------------------------------- #[test] diff --git a/tests/ui/app_error/fail/enum_missing_variant.stderr b/tests/ui/app_error/fail/enum_missing_variant.stderr index bbc297c..d000de1 100644 --- a/tests/ui/app_error/fail/enum_missing_variant.stderr +++ b/tests/ui/app_error/fail/enum_missing_variant.stderr @@ -1,9 +1,8 @@ error: all variants must use #[app_error(...)] to derive AppError conversion --> tests/ui/app_error/fail/enum_missing_variant.rs:8:5 | -8 | / #[error("without")] -9 | | Without, - | |___________^ +8 | #[error("without")] + | ^ warning: unused import: `AppErrorKind` --> tests/ui/app_error/fail/enum_missing_variant.rs:1:17 @@ -11,4 +10,4 @@ warning: unused import: `AppErrorKind` 1 | use masterror::{AppErrorKind, Error}; | ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/app_error/fail/missing_code.stderr b/tests/ui/app_error/fail/missing_code.stderr index 4f02301..70ccade 100644 --- a/tests/ui/app_error/fail/missing_code.stderr +++ b/tests/ui/app_error/fail/missing_code.stderr @@ -2,7 +2,7 @@ error: AppCode conversion requires `code = ...` in #[app_error(...)] --> tests/ui/app_error/fail/missing_code.rs:9:5 | 9 | #[app_error(kind = AppErrorKind::Service)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ warning: unused imports: `AppCode` and `AppErrorKind` --> tests/ui/app_error/fail/missing_code.rs:1:17 @@ -10,4 +10,4 @@ warning: unused imports: `AppCode` and `AppErrorKind` 1 | use masterror::{AppCode, AppErrorKind, Error}; | ^^^^^^^ ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/app_error/fail/missing_kind.stderr b/tests/ui/app_error/fail/missing_kind.stderr index 021c135..c615e98 100644 --- a/tests/ui/app_error/fail/missing_kind.stderr +++ b/tests/ui/app_error/fail/missing_kind.stderr @@ -2,4 +2,4 @@ error: missing `kind = ...` in #[app_error(...)] --> tests/ui/app_error/fail/missing_kind.rs:5:1 | 5 | #[app_error(message)] - | ^^^^^^^^^^^^^^^^^^^^^ + | ^ diff --git a/tests/ui/formatter/fail/duplicate_fmt.stderr b/tests/ui/formatter/fail/duplicate_fmt.stderr index 5b8f363..5b08225 100644 --- a/tests/ui/formatter/fail/duplicate_fmt.stderr +++ b/tests/ui/formatter/fail/duplicate_fmt.stderr @@ -2,4 +2,4 @@ error: duplicate `fmt` handler specified --> tests/ui/formatter/fail/duplicate_fmt.rs:4:36 | 4 | #[error(fmt = crate::format_error, fmt = crate::format_error)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^ diff --git a/tests/ui/formatter/fail/implicit_after_named.stderr b/tests/ui/formatter/fail/implicit_after_named.stderr index be76742..d416399 100644 --- a/tests/ui/formatter/fail/implicit_after_named.stderr +++ b/tests/ui/formatter/fail/implicit_after_named.stderr @@ -8,5 +8,4 @@ error: multiple unused formatting arguments | argument never used | argument never used | - = note: consider adding 2 format specifiers = note: this error originates in the derive macro `Error` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/formatter/fail/unsupported_flag.stderr b/tests/ui/formatter/fail/unsupported_flag.stderr index b8bf229..d7acdb1 100644 --- a/tests/ui/formatter/fail/unsupported_flag.stderr +++ b/tests/ui/formatter/fail/unsupported_flag.stderr @@ -1,5 +1,5 @@ error: placeholder spanning bytes 0..11 uses an unsupported formatter - --> tests/ui/formatter/fail/unsupported_flag.rs:4:10 + --> tests/ui/formatter/fail/unsupported_flag.rs:4:9 | 4 | #[error("{value:##x}")] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^^^ diff --git a/tests/ui/formatter/fail/unsupported_formatter.stderr b/tests/ui/formatter/fail/unsupported_formatter.stderr index a6a40c2..5869420 100644 --- a/tests/ui/formatter/fail/unsupported_formatter.stderr +++ b/tests/ui/formatter/fail/unsupported_formatter.stderr @@ -1,5 +1,5 @@ error: placeholder spanning bytes 0..9 uses an unsupported formatter - --> tests/ui/formatter/fail/unsupported_formatter.rs:4:10 + --> tests/ui/formatter/fail/unsupported_formatter.rs:4:9 | 4 | #[error("{value:y}")] - | ^^^^^^^^^ + | ^^^^^^^^^^^ diff --git a/tests/ui/formatter/fail/uppercase_binary.stderr b/tests/ui/formatter/fail/uppercase_binary.stderr index 3d332c7..bbe04b4 100644 --- a/tests/ui/formatter/fail/uppercase_binary.stderr +++ b/tests/ui/formatter/fail/uppercase_binary.stderr @@ -1,5 +1,5 @@ error: placeholder spanning bytes 0..9 uses an unsupported formatter - --> tests/ui/formatter/fail/uppercase_binary.rs:4:10 + --> tests/ui/formatter/fail/uppercase_binary.rs:4:9 | 4 | #[error("{value:B}")] - | ^^^^^^^^^ + | ^^^^^^^^^^^ diff --git a/tests/ui/formatter/fail/uppercase_pointer.stderr b/tests/ui/formatter/fail/uppercase_pointer.stderr index 0bd10fa..2c30e71 100644 --- a/tests/ui/formatter/fail/uppercase_pointer.stderr +++ b/tests/ui/formatter/fail/uppercase_pointer.stderr @@ -1,5 +1,5 @@ error: placeholder spanning bytes 0..9 uses an unsupported formatter - --> tests/ui/formatter/fail/uppercase_pointer.rs:4:10 + --> tests/ui/formatter/fail/uppercase_pointer.rs:4:9 | 4 | #[error("{value:P}")] - | ^^^^^^^^^ + | ^^^^^^^^^^^ diff --git a/tests/ui/masterror/fail/duplicate_attr.stderr b/tests/ui/masterror/fail/duplicate_attr.stderr index 113a10d..c3fb86b 100644 --- a/tests/ui/masterror/fail/duplicate_attr.stderr +++ b/tests/ui/masterror/fail/duplicate_attr.stderr @@ -10,4 +10,4 @@ warning: unused imports: `AppCode` and `AppErrorKind` 1 | use masterror::{AppCode, AppErrorKind, Masterror}; | ^^^^^^^ ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/masterror/fail/duplicate_telemetry.stderr b/tests/ui/masterror/fail/duplicate_telemetry.stderr index 9ada290..b331baa 100644 --- a/tests/ui/masterror/fail/duplicate_telemetry.stderr +++ b/tests/ui/masterror/fail/duplicate_telemetry.stderr @@ -10,4 +10,4 @@ warning: unused imports: `AppCode` and `AppErrorKind` 1 | use masterror::{AppCode, AppErrorKind, Masterror}; | ^^^^^^^ ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/masterror/fail/empty_redact.stderr b/tests/ui/masterror/fail/empty_redact.stderr index fd151cc..b2658a1 100644 --- a/tests/ui/masterror/fail/empty_redact.stderr +++ b/tests/ui/masterror/fail/empty_redact.stderr @@ -10,4 +10,4 @@ warning: unused imports: `AppCode` and `AppErrorKind` 1 | use masterror::{AppCode, AppErrorKind, Masterror}; | ^^^^^^^ ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/masterror/fail/enum_missing_variant.stderr b/tests/ui/masterror/fail/enum_missing_variant.stderr index 5a25e12..83d517f 100644 --- a/tests/ui/masterror/fail/enum_missing_variant.stderr +++ b/tests/ui/masterror/fail/enum_missing_variant.stderr @@ -1,9 +1,8 @@ error: all variants must use #[masterror(...)] to derive masterror::Error conversion --> tests/ui/masterror/fail/enum_missing_variant.rs:8:5 | -8 | / #[error("missing")] -9 | | Missing - | |___________^ +8 | #[error("missing")] + | ^ warning: unused imports: `AppCode` and `AppErrorKind` --> tests/ui/masterror/fail/enum_missing_variant.rs:1:17 @@ -11,4 +10,4 @@ warning: unused imports: `AppCode` and `AppErrorKind` 1 | use masterror::{AppCode, AppErrorKind, Masterror}; | ^^^^^^^ ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/masterror/fail/missing_category.stderr b/tests/ui/masterror/fail/missing_category.stderr index bdadf45..f929951 100644 --- a/tests/ui/masterror/fail/missing_category.stderr +++ b/tests/ui/masterror/fail/missing_category.stderr @@ -2,7 +2,7 @@ error: missing `category = ...` in #[masterror(...)] --> tests/ui/masterror/fail/missing_category.rs:5:1 | 5 | #[masterror(code = AppCode::Internal)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ warning: unused import: `AppCode` --> tests/ui/masterror/fail/missing_category.rs:1:17 @@ -10,4 +10,4 @@ warning: unused import: `AppCode` 1 | use masterror::{AppCode, Masterror}; | ^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/masterror/fail/missing_code.stderr b/tests/ui/masterror/fail/missing_code.stderr index 037fac8..34abc91 100644 --- a/tests/ui/masterror/fail/missing_code.stderr +++ b/tests/ui/masterror/fail/missing_code.stderr @@ -2,7 +2,7 @@ error: missing `code = ...` in #[masterror(...)] --> tests/ui/masterror/fail/missing_code.rs:5:1 | 5 | #[masterror(category = AppErrorKind::Internal)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ warning: unused import: `AppErrorKind` --> tests/ui/masterror/fail/missing_code.rs:1:17 @@ -10,4 +10,4 @@ warning: unused import: `AppErrorKind` 1 | use masterror::{AppErrorKind, Masterror}; | ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/masterror/fail/unknown_option.stderr b/tests/ui/masterror/fail/unknown_option.stderr index 1822edf..d579838 100644 --- a/tests/ui/masterror/fail/unknown_option.stderr +++ b/tests/ui/masterror/fail/unknown_option.stderr @@ -10,4 +10,4 @@ warning: unused imports: `AppCode` and `AppErrorKind` 1 | use masterror::{AppCode, AppErrorKind, Masterror}; | ^^^^^^^ ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default