Skip to content

Commit 6e04e91

Browse files
committed
Response: add Error storage, retrieval, conversion
This allows for robust creation of Response-s directly from Error-s, with error capture for future reference, and retrieval via `error() -> Option<&Error>`. PR-URL: #174 Refs: #169 Refs: http-rs/tide#546 Refs: http-rs/tide#532 Refs: http-rs/tide#452
1 parent 0836aa5 commit 6e04e91

File tree

2 files changed

+91
-4
lines changed

2 files changed

+91
-4
lines changed

src/response.rs

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::headers::{
1515
};
1616
use crate::mime::Mime;
1717
use crate::trailers::{self, Trailers};
18-
use crate::{Body, Extensions, StatusCode, Version};
18+
use crate::{Body, Error, Extensions, StatusCode, Version};
1919

2020
cfg_unstable! {
2121
use crate::upgrade;
@@ -50,6 +50,7 @@ pin_project_lite::pin_project! {
5050
ext: Extensions,
5151
local_addr: Option<String>,
5252
peer_addr: Option<String>,
53+
error: Option<Error>,
5354
}
5455
}
5556

@@ -85,6 +86,7 @@ pin_project_lite::pin_project! {
8586
ext: Extensions,
8687
local_addr: Option<String>,
8788
peer_addr: Option<String>,
89+
error: Option<Error>,
8890
}
8991
}
9092

@@ -111,6 +113,7 @@ impl Response {
111113
ext: Extensions::new(),
112114
peer_addr: None,
113115
local_addr: None,
116+
error: None,
114117
}
115118
}
116119

@@ -140,6 +143,7 @@ impl Response {
140143
ext: Extensions::new(),
141144
peer_addr: None,
142145
local_addr: None,
146+
error: None,
143147
}
144148
}
145149

@@ -469,6 +473,16 @@ impl Response {
469473
self.body.is_empty()
470474
}
471475

476+
/// Returns an optional reference to the `Error` if the response was created from one, or else `None`.
477+
pub fn error(&self) -> Option<&Error> {
478+
self.error.as_ref()
479+
}
480+
481+
/// Takes the `Error` from the response if one exists, replacing it with `None`.
482+
pub fn take_error(&mut self) -> Option<Error> {
483+
self.error.take()
484+
}
485+
472486
/// Get the HTTP version, if one has been set.
473487
///
474488
/// # Examples
@@ -641,8 +655,10 @@ impl Response {
641655
}
642656

643657
impl Clone for Response {
644-
/// Clone the response, resolving the body to `Body::empty()` and removing
645-
/// extensions.
658+
/// Clone the response, with some exceptions:
659+
/// - The body is resolved to `Body::empty()`.
660+
/// - Any attached extensions are not cloned.
661+
/// - Any attached error is not cloned.
646662
fn clone(&self) -> Self {
647663
Self {
648664
status: self.status.clone(),
@@ -661,6 +677,7 @@ impl Clone for Response {
661677
ext: Extensions::new(),
662678
peer_addr: self.peer_addr.clone(),
663679
local_addr: self.local_addr.clone(),
680+
error: None,
664681
}
665682
}
666683
}
@@ -733,6 +750,58 @@ impl Index<&str> for Response {
733750
}
734751
}
735752

753+
#[cfg(not(feature = "unstable"))]
754+
impl From<Error> for Response {
755+
/// Create a new response from an `http_types::Error`.
756+
///
757+
/// This will store the error in the `Response`, allowing it to later be
758+
/// checked via `Response::error()`.
759+
fn from(error: Error) -> Self {
760+
let (trailers_sender, trailers_receiver) = sync::channel(1);
761+
Self {
762+
status: error.status(),
763+
headers: Headers::new(),
764+
version: None,
765+
body: Body::empty(),
766+
trailers_sender: Some(trailers_sender),
767+
trailers_receiver: Some(trailers_receiver),
768+
has_trailers: false,
769+
ext: Extensions::new(),
770+
peer_addr: None,
771+
local_addr: None,
772+
error: Some(error),
773+
}
774+
}
775+
}
776+
777+
#[cfg(feature = "unstable")]
778+
impl From<Error> for Response {
779+
/// Create a new response from an `http_types::Error`.
780+
///
781+
/// This will store the error in the `Response`, allowing it to later be
782+
/// checked via `Response::error()`.
783+
fn from(error: Error) -> Self {
784+
let (trailers_sender, trailers_receiver) = sync::channel(1);
785+
let (upgrade_sender, upgrade_receiver) = sync::channel(1);
786+
Self {
787+
status: error.status(),
788+
headers: Headers::new(),
789+
version: None,
790+
body: Body::empty(),
791+
trailers_sender: Some(trailers_sender),
792+
trailers_receiver: Some(trailers_receiver),
793+
has_trailers: false,
794+
upgrade_sender: Some(upgrade_sender),
795+
upgrade_receiver: Some(upgrade_receiver),
796+
has_upgrade: false,
797+
ext: Extensions::new(),
798+
peer_addr: None,
799+
local_addr: None,
800+
error: Some(error),
801+
}
802+
}
803+
}
804+
736805
impl From<StatusCode> for Response {
737806
fn from(s: StatusCode) -> Self {
738807
Response::new(s)

tests/error.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use http_types::{bail, ensure, ensure_eq, Error, StatusCode};
1+
use http_types::{bail, ensure, ensure_eq, Error, Response, StatusCode};
22
use std::io;
33

44
#[test]
@@ -71,3 +71,21 @@ fn option_ext() {
7171
let err = res.unwrap_err();
7272
assert_eq!(err.status(), StatusCode::NotFound);
7373
}
74+
75+
#[test]
76+
fn to_response() {
77+
let msg = "This is an error";
78+
79+
let error = Error::from_str(StatusCode::NotFound, msg);
80+
let mut res: Response = error.into();
81+
82+
assert!(res.error().is_some());
83+
// Ensure we did not consume the error
84+
assert!(res.error().is_some());
85+
86+
assert_eq!(res.error().unwrap().status(), StatusCode::NotFound);
87+
assert_eq!(res.error().unwrap().to_string(), msg);
88+
89+
res.take_error();
90+
assert!(res.error().is_none());
91+
}

0 commit comments

Comments
 (0)