diff --git a/src/app_error.rs b/src/app_error.rs index 15fa3f2..1cbb79d 100644 --- a/src/app_error.rs +++ b/src/app_error.rs @@ -57,6 +57,8 @@ //! `kind`, `code` and optional `message` fields. Prefer calling it at the //! transport boundary (e.g. in `IntoResponse`) to avoid duplicate logs. +use std::borrow::Cow; + use thiserror::Error; use tracing::error; @@ -73,7 +75,7 @@ pub struct AppError { /// Semantic category of the error. pub kind: AppErrorKind, /// Optional, public-friendly message. - pub message: Option + pub message: Option> } /// Conventional result alias for application code. @@ -92,7 +94,7 @@ impl AppError { /// let err = AppError::new(AppErrorKind::BadRequest, "invalid payload"); /// assert!(err.message.is_some()); /// ``` - pub fn new(kind: AppErrorKind, msg: impl Into) -> Self { + pub fn new(kind: AppErrorKind, msg: impl Into>) -> Self { Self::with(kind, msg) } @@ -100,7 +102,7 @@ impl AppError { /// /// Prefer named helpers (e.g. [`AppError::not_found`]) where it clarifies /// intent. - pub fn with(kind: AppErrorKind, msg: impl Into) -> Self { + pub fn with(kind: AppErrorKind, msg: impl Into>) -> Self { Self { kind, message: Some(msg.into()) @@ -142,103 +144,103 @@ impl AppError { // 4xx-ish /// Build a `NotFound` error. - pub fn not_found(msg: impl Into) -> Self { + pub fn not_found(msg: impl Into>) -> Self { Self::with(AppErrorKind::NotFound, msg) } /// Build a `Validation` error. - pub fn validation(msg: impl Into) -> Self { + pub fn validation(msg: impl Into>) -> Self { Self::with(AppErrorKind::Validation, msg) } /// Build an `Unauthorized` error. - pub fn unauthorized(msg: impl Into) -> Self { + pub fn unauthorized(msg: impl Into>) -> Self { Self::with(AppErrorKind::Unauthorized, msg) } /// Build a `Forbidden` error. - pub fn forbidden(msg: impl Into) -> Self { + pub fn forbidden(msg: impl Into>) -> Self { Self::with(AppErrorKind::Forbidden, msg) } /// Build a `Conflict` error. - pub fn conflict(msg: impl Into) -> Self { + pub fn conflict(msg: impl Into>) -> Self { Self::with(AppErrorKind::Conflict, msg) } /// Build a `BadRequest` error. - pub fn bad_request(msg: impl Into) -> Self { + pub fn bad_request(msg: impl Into>) -> Self { Self::with(AppErrorKind::BadRequest, msg) } /// Build a `RateLimited` error. - pub fn rate_limited(msg: impl Into) -> Self { + pub fn rate_limited(msg: impl Into>) -> Self { Self::with(AppErrorKind::RateLimited, msg) } /// Build a `TelegramAuth` error. - pub fn telegram_auth(msg: impl Into) -> Self { + pub fn telegram_auth(msg: impl Into>) -> Self { Self::with(AppErrorKind::TelegramAuth, msg) } // 5xx-ish /// Build an `Internal` error. - pub fn internal(msg: impl Into) -> Self { + pub fn internal(msg: impl Into>) -> Self { Self::with(AppErrorKind::Internal, msg) } /// Build a `Service` error (generic server-side service failure). - pub fn service(msg: impl Into) -> Self { + pub fn service(msg: impl Into>) -> Self { Self::with(AppErrorKind::Service, msg) } /// Build a `Database` error with an optional message. /// /// Accepts `Option` to avoid gratuitous `.map(|...| ...)` at call sites /// when you may or may not have a safe-to-print string at hand. - pub fn database(msg: Option>) -> Self { + pub fn database(msg: Option>>) -> Self { Self { kind: AppErrorKind::Database, - message: msg.map(|m| m.into()) + message: msg.map(Into::into) } } /// Build a `Config` error. - pub fn config(msg: impl Into) -> Self { + pub fn config(msg: impl Into>) -> Self { Self::with(AppErrorKind::Config, msg) } /// Build a `Turnkey` error. - pub fn turnkey(msg: impl Into) -> Self { + pub fn turnkey(msg: impl Into>) -> Self { Self::with(AppErrorKind::Turnkey, msg) } // Infra / network /// Build a `Timeout` error. - pub fn timeout(msg: impl Into) -> Self { + pub fn timeout(msg: impl Into>) -> Self { Self::with(AppErrorKind::Timeout, msg) } /// Build a `Network` error. - pub fn network(msg: impl Into) -> Self { + pub fn network(msg: impl Into>) -> Self { Self::with(AppErrorKind::Network, msg) } /// Build a `DependencyUnavailable` error. - pub fn dependency_unavailable(msg: impl Into) -> Self { + pub fn dependency_unavailable(msg: impl Into>) -> Self { Self::with(AppErrorKind::DependencyUnavailable, msg) } /// Backward-compatible alias; routes to `DependencyUnavailable`. - pub fn service_unavailable(msg: impl Into) -> Self { + pub fn service_unavailable(msg: impl Into>) -> Self { Self::with(AppErrorKind::DependencyUnavailable, msg) } // Serialization / external API / subsystems /// Build a `Serialization` error. - pub fn serialization(msg: impl Into) -> Self { + pub fn serialization(msg: impl Into>) -> Self { Self::with(AppErrorKind::Serialization, msg) } /// Build a `Deserialization` error. - pub fn deserialization(msg: impl Into) -> Self { + pub fn deserialization(msg: impl Into>) -> Self { Self::with(AppErrorKind::Deserialization, msg) } /// Build an `ExternalApi` error. - pub fn external_api(msg: impl Into) -> Self { + pub fn external_api(msg: impl Into>) -> Self { Self::with(AppErrorKind::ExternalApi, msg) } /// Build a `Queue` error. - pub fn queue(msg: impl Into) -> Self { + pub fn queue(msg: impl Into>) -> Self { Self::with(AppErrorKind::Queue, msg) } /// Build a `Cache` error. - pub fn cache(msg: impl Into) -> Self { + pub fn cache(msg: impl Into>) -> Self { Self::with(AppErrorKind::Cache, msg) } } diff --git a/src/convert/reqwest.rs b/src/convert/reqwest.rs index 4a632bc..19256e2 100644 --- a/src/convert/reqwest.rs +++ b/src/convert/reqwest.rs @@ -104,7 +104,10 @@ mod tests { let err_str = err.to_string(); let app_err: AppError = err.into(); let msg = app_err.message.expect("app error message"); - assert!(msg.contains(&err_str), "{msg} does not contain {err_str}"); + assert!( + msg.contains(err_str.as_str()), + "{msg} does not contain {err_str}" + ); server.abort(); } diff --git a/src/response.rs b/src/response.rs index 391ca3f..edd79fe 100644 --- a/src/response.rs +++ b/src/response.rs @@ -60,8 +60,8 @@ //! provided as a deprecated shim. use std::{ - fmt::{Display, Formatter, Result as FmtResult}, - time::Duration + borrow::Cow, + fmt::{Display, Formatter, Result as FmtResult} }; use http::StatusCode; @@ -297,9 +297,9 @@ impl From<&AppError> for ErrorResponse { let message = err .message - .as_deref() - .unwrap_or("An error occurred") - .to_owned(); + .clone() + .unwrap_or(Cow::Borrowed("An error occurred")) + .into_owned(); Self { status,