From 8084b242d5b78e53267e63c693dadf71e313320c Mon Sep 17 00:00:00 2001
From: Nathaniel McCallum <nathaniel@profian.com>
Date: Thu, 21 Apr 2022 10:24:29 -0400
Subject: [PATCH] Add `response::ErrorResponse` and `response::Result` (#921)

* feat: add response::{Error, Result}

This type makes for efficient use of the `?` operator when in a function
with multiple return error types that all implement `IntoResponse`.

Signed-off-by: Nathaniel McCallum <nathaniel@profian.com>

* misc adjustments from PR review

* Rename to `ErrorResponse` and `ResultResponse`

* nitpicky docs changes

* update changelog

* changelog wording

* Apply suggestions from code review

Co-authored-by: Jonas Platte <jplatte+git@posteo.de>

Co-authored-by: David Pedersen <david.pdrsn@gmail.com>
Co-authored-by: Jonas Platte <jplatte+git@posteo.de>
---
 axum-core/CHANGELOG.md        |   5 +-
 axum-core/src/response/mod.rs | 108 ++++++++++++++++++++++++++++++++++
 axum/CHANGELOG.md             |   5 +-
 axum/src/response/mod.rs      |   2 +-
 4 files changed, 117 insertions(+), 3 deletions(-)

diff --git a/axum-core/CHANGELOG.md b/axum-core/CHANGELOG.md
index 879e8e6d..e4173599 100644
--- a/axum-core/CHANGELOG.md
+++ b/axum-core/CHANGELOG.md
@@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 # Unreleased
 
-- None.
+- **added:** Add `response::ErrorResponse` and `response::Result` for
+  `IntoResponse`-based error handling ([#921])
+
+[#921]: https://github.com/tokio-rs/axum/pull/921 
 
 # 0.2.2 (19. April, 2022)
 
diff --git a/axum-core/src/response/mod.rs b/axum-core/src/response/mod.rs
index 6a15ba73..d66dfec5 100644
--- a/axum-core/src/response/mod.rs
+++ b/axum-core/src/response/mod.rs
@@ -19,3 +19,111 @@ pub use self::{
 /// Type alias for [`http::Response`] whose body type defaults to [`BoxBody`], the most common body
 /// type used with axum.
 pub type Response<T = BoxBody> = http::Response<T>;
+
+/// An [`IntoResponse`]-based result type that uses [`ErrorResponse`] as the error type.
+///
+/// All types which implement [`IntoResponse`] can be converted to an [`ErrorResponse`]. This makes
+/// it useful as a general purpose error type for functions which combine multiple distinct error
+/// types that all implement [`IntoResponse`].
+///
+/// # Example
+///
+/// ```
+/// use axum::{
+///     response::{IntoResponse, Response},
+///     http::StatusCode,
+/// };
+///
+/// // two fallible functions with different error types
+/// fn try_something() -> Result<(), ErrorA> {
+///     // ...
+///     # unimplemented!()
+/// }
+///
+/// fn try_something_else() -> Result<(), ErrorB> {
+///     // ...
+///     # unimplemented!()
+/// }
+///
+/// // each error type implements `IntoResponse`
+/// struct ErrorA;
+///
+/// impl IntoResponse for ErrorA {
+///     fn into_response(self) -> Response {
+///         // ...
+///         # unimplemented!()
+///     }
+/// }
+///
+/// enum ErrorB {
+///     SomethingWentWrong,
+/// }
+///
+/// impl IntoResponse for ErrorB {
+///     fn into_response(self) -> Response {
+///         // ...
+///         # unimplemented!()
+///     }
+/// }
+///
+/// // we can combine them using `axum::response::Result` and still use `?`
+/// async fn handler() -> axum::response::Result<&'static str> {
+///     // the errors are automatically converted to `ErrorResponse`
+///     try_something()?;
+///     try_something_else()?;
+///
+///     Ok("it worked!")
+/// }
+/// ```
+///
+/// # As a replacement for `std::result::Result`
+///
+/// Since `axum::response::Result` has a default error type you only have to specify the `Ok` type:
+///
+/// ```
+/// use axum::{
+///     response::{IntoResponse, Response, Result},
+///     http::StatusCode,
+/// };
+///
+/// // `Result<T>` automatically uses `ErrorResponse` as the error type.
+/// async fn handler() -> Result<&'static str> {
+///     try_something()?;
+///
+///     Ok("it worked!")
+/// }
+///
+/// // You can still specify the error even if you've imported `axum::response::Result`
+/// fn try_something() -> Result<(), StatusCode> {
+///     // ...
+///     # unimplemented!()
+/// }
+/// ```
+pub type Result<T, E = ErrorResponse> = std::result::Result<T, E>;
+
+impl<T> IntoResponse for Result<T>
+where
+    T: IntoResponse,
+{
+    fn into_response(self) -> Response {
+        match self {
+            Ok(ok) => ok.into_response(),
+            Err(err) => err.0,
+        }
+    }
+}
+
+/// An [`IntoResponse`]-based error type
+///
+/// See [`Result`] for more details.
+#[derive(Debug)]
+pub struct ErrorResponse(Response);
+
+impl<T> From<T> for ErrorResponse
+where
+    T: IntoResponse,
+{
+    fn from(value: T) -> Self {
+        Self(value.into_response())
+    }
+}
diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md
index 8e3efd53..37a98efb 100644
--- a/axum/CHANGELOG.md
+++ b/axum/CHANGELOG.md
@@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 # Unreleased
 
-- None.
+- **added:** Add `response::ErrorResponse` and `response::Result` for
+  `IntoResponse`-based error handling ([#921])
+
+[#921]: https://github.com/tokio-rs/axum/pull/921 
 
 # 0.5.3 (19. April, 2022)
 
diff --git a/axum/src/response/mod.rs b/axum/src/response/mod.rs
index d8fade41..50c2c458 100644
--- a/axum/src/response/mod.rs
+++ b/axum/src/response/mod.rs
@@ -20,7 +20,7 @@ pub use crate::Extension;
 
 #[doc(inline)]
 pub use axum_core::response::{
-    AppendHeaders, IntoResponse, IntoResponseParts, Response, ResponseParts,
+    AppendHeaders, ErrorResponse, IntoResponse, IntoResponseParts, Response, ResponseParts, Result,
 };
 
 #[doc(inline)]