Skip to content

Commit 08ee3d2

Browse files
authored
Merge pull request #157 from zeroed/status-takes-tryinto-statuscode
Status should take TryInto<StatusCode>
2 parents a9357ed + a2e58a1 commit 08ee3d2

File tree

3 files changed

+305
-179
lines changed

3 files changed

+305
-179
lines changed

src/response.rs

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ impl Response {
9898
/// ```
9999
/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
100100
/// #
101-
/// use http_types::{Url, Method, Response, StatusCode};
101+
/// use http_types::{Method, Response, StatusCode, Url};
102102
///
103103
/// let mut req = Response::new(StatusCode::Ok);
104104
/// req.insert_header("Content-Type", "text/plain");
@@ -115,8 +115,9 @@ impl Response {
115115

116116
/// Append a header to the headers.
117117
///
118-
/// Unlike `insert` this function will not override the contents of a header, but insert a
119-
/// header if there aren't any. Or else append to the existing list of headers.
118+
/// Unlike `insert` this function will not override the contents of a
119+
/// header, but insert a header if there aren't any. Or else append to
120+
/// the existing list of headers.
120121
///
121122
/// # Examples
122123
///
@@ -161,7 +162,7 @@ impl Response {
161162
/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
162163
/// # async_std::task::block_on(async {
163164
/// #
164-
/// use http_types::{Body, Url, Method, Response, StatusCode};
165+
/// use http_types::{Body, Method, Response, StatusCode, Url};
165166
///
166167
/// let mut req = Response::new(StatusCode::Ok);
167168
/// req.set_body("Hello, Nori!");
@@ -190,7 +191,7 @@ impl Response {
190191
/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
191192
/// # async_std::task::block_on(async {
192193
/// #
193-
/// use http_types::{Body, Url, Method, Response, StatusCode};
194+
/// use http_types::{Body, Method, Response, StatusCode, Url};
194195
///
195196
/// let mut req = Response::new(StatusCode::Ok);
196197
/// req.set_body("Hello, Nori!");
@@ -218,7 +219,7 @@ impl Response {
218219
/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
219220
/// # async_std::task::block_on(async {
220221
/// #
221-
/// use http_types::{Body, Url, Method, Response, StatusCode};
222+
/// use http_types::{Body, Method, Response, StatusCode, Url};
222223
///
223224
/// let mut req = Response::new(StatusCode::Ok);
224225
/// req.set_body("Hello, Nori!");
@@ -251,10 +252,10 @@ impl Response {
251252
/// # use std::io::prelude::*;
252253
/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
253254
/// # async_std::task::block_on(async {
254-
/// use http_types::{Body, Url, Method, Response, StatusCode};
255255
/// use async_std::io::Cursor;
256+
/// use http_types::{Body, Method, Response, StatusCode, Url};
256257
///
257-
/// let mut res = Response::new(StatusCode::Ok);
258+
/// let mut res = Response::new(StatusCode::Ok);
258259
/// let cursor = Cursor::new("Hello Nori");
259260
/// let body = Body::from_reader(cursor, None);
260261
/// res.set_body(body);
@@ -277,7 +278,7 @@ impl Response {
277278
///
278279
/// ```
279280
/// # fn main() -> Result<(), http_types::Error> { async_std::task::block_on(async {
280-
/// use http_types::{Body, Url, Method, Response, StatusCode};
281+
/// use http_types::{Body, Method, Response, StatusCode, Url};
281282
///
282283
/// let bytes = vec![1, 2, 3];
283284
/// let mut res = Response::new(StatusCode::Ok);
@@ -303,14 +304,18 @@ impl Response {
303304
///
304305
/// ```
305306
/// # fn main() -> Result<(), http_types::Error> { async_std::task::block_on(async {
306-
/// use http_types::{Body, Url, Method, Response, StatusCode};
307-
/// use http_types::convert::{Serialize, Deserialize};
307+
/// use http_types::convert::{Deserialize, Serialize};
308+
/// use http_types::{Body, Method, Response, StatusCode, Url};
308309
///
309310
/// #[derive(Debug, Serialize, Deserialize)]
310-
/// struct Cat { name: String }
311+
/// struct Cat {
312+
/// name: String,
313+
/// }
311314
///
312-
/// let cat = Cat { name: String::from("chashu") };
313-
/// let mut res = Response::new(StatusCode::Ok);
315+
/// let cat = Cat {
316+
/// name: String::from("chashu"),
317+
/// };
318+
/// let mut res = Response::new(StatusCode::Ok);
314319
/// res.set_body(Body::from_json(&cat)?);
315320
///
316321
/// let cat: Cat = res.body_json().await?;
@@ -333,14 +338,18 @@ impl Response {
333338
///
334339
/// ```
335340
/// # fn main() -> Result<(), http_types::Error> { async_std::task::block_on(async {
336-
/// use http_types::{Body, Url, Method, Response, StatusCode};
337-
/// use http_types::convert::{Serialize, Deserialize};
341+
/// use http_types::convert::{Deserialize, Serialize};
342+
/// use http_types::{Body, Method, Response, StatusCode, Url};
338343
///
339344
/// #[derive(Debug, Serialize, Deserialize)]
340-
/// struct Cat { name: String }
345+
/// struct Cat {
346+
/// name: String,
347+
/// }
341348
///
342-
/// let cat = Cat { name: String::from("chashu") };
343-
/// let mut res = Response::new(StatusCode::Ok);
349+
/// let cat = Cat {
350+
/// name: String::from("chashu"),
351+
/// };
352+
/// let mut res = Response::new(StatusCode::Ok);
344353
/// res.set_body(Body::from_form(&cat)?);
345354
///
346355
/// let cat: Cat = res.body_form().await?;
@@ -374,14 +383,16 @@ impl Response {
374383

375384
/// Get the length of the body stream, if it has been set.
376385
///
377-
/// This value is set when passing a fixed-size object into as the body. E.g. a string, or a
378-
/// buffer. Consumers of this API should check this value to decide whether to use `Chunked`
379-
/// encoding, or set the response length.
386+
/// This value is set when passing a fixed-size object into as the body.
387+
/// E.g. a string, or a buffer. Consumers of this API should check this
388+
/// value to decide whether to use `Chunked` encoding, or set the
389+
/// response length.
380390
pub fn len(&self) -> Option<usize> {
381391
self.body.len()
382392
}
383393

384-
/// Returns `true` if the set length of the body stream is zero, `false` otherwise.
394+
/// Returns `true` if the set length of the body stream is zero, `false`
395+
/// otherwise.
385396
pub fn is_empty(&self) -> Option<bool> {
386397
self.body.is_empty()
387398
}
@@ -426,7 +437,8 @@ impl Response {
426437
self.peer_addr.as_deref()
427438
}
428439

429-
/// Get the local socket address for the underlying transport, if appropriate
440+
/// Get the local socket address for the underlying transport, if
441+
/// appropriate
430442
pub fn local_addr(&self) -> Option<&str> {
431443
self.local_addr.as_deref()
432444
}
@@ -477,8 +489,8 @@ impl Response {
477489
self.headers.iter()
478490
}
479491

480-
/// An iterator visiting all header pairs in arbitrary order, with mutable references to the
481-
/// values.
492+
/// An iterator visiting all header pairs in arbitrary order, with mutable
493+
/// references to the values.
482494
pub fn iter_mut(&mut self) -> headers::IterMut<'_> {
483495
self.headers.iter_mut()
484496
}
@@ -506,7 +518,7 @@ impl Response {
506518
/// ```
507519
/// # fn main() -> Result<(), http_types::Error> {
508520
/// #
509-
/// use http_types::{StatusCode, Response, Version};
521+
/// use http_types::{Response, StatusCode, Version};
510522
///
511523
/// let mut res = Response::new(StatusCode::Ok);
512524
/// res.ext_mut().insert("hello from the extension");
@@ -520,7 +532,8 @@ impl Response {
520532
}
521533

522534
impl Clone for Response {
523-
/// Clone the response, resolving the body to `Body::empty()` and removing extensions.
535+
/// Clone the response, resolving the body to `Body::empty()` and removing
536+
/// extensions.
524537
fn clone(&self) -> Self {
525538
Self {
526539
status: self.status.clone(),
@@ -656,8 +669,15 @@ impl<'a> IntoIterator for &'a mut Response {
656669
#[cfg(test)]
657670
mod test {
658671
use super::Response;
672+
659673
#[test]
660-
fn construct_shorthand() {
674+
fn construct_shorthand_with_valid_status_code() {
661675
let _res = Response::new(200);
662676
}
677+
678+
#[test]
679+
#[should_panic(expected = "Could not convert into a valid `StatusCode`")]
680+
fn construct_shorthand_with_invalid_status_code() {
681+
let _res = Response::new(600);
682+
}
663683
}

src/status.rs

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::{Error, StatusCode};
2-
use core::convert::{Infallible, Into};
2+
use core::convert::{Infallible, TryInto};
33
use std::error::Error as StdError;
4+
use std::fmt::Debug;
45

56
/// Provides the `status` method for `Result` and `Option`.
67
///
@@ -9,60 +10,108 @@ pub trait Status<T, E>: private::Sealed {
910
/// Wrap the error value with an additional status code.
1011
fn status<S>(self, status: S) -> Result<T, Error>
1112
where
12-
S: Into<StatusCode>;
13+
S: TryInto<StatusCode>,
14+
S::Error: Debug;
1315

1416
/// Wrap the error value with an additional status code that is evaluated
1517
/// lazily only once an error does occur.
1618
fn with_status<S, F>(self, f: F) -> Result<T, Error>
1719
where
18-
S: Into<StatusCode>,
20+
S: TryInto<StatusCode>,
21+
S::Error: Debug,
1922
F: FnOnce() -> S;
2023
}
2124

2225
impl<T, E> Status<T, E> for Result<T, E>
2326
where
2427
E: StdError + Send + Sync + 'static,
2528
{
29+
/// Wrap the error value with an additional status code.
30+
///
31+
/// # Panics
32+
///
33+
/// Panics if [`Status`][status] is not a valid [`StatusCode`][statuscode].
34+
///
35+
/// [status]: crate::Status
36+
/// [statuscode]: crate::StatusCode
2637
fn status<S>(self, status: S) -> Result<T, Error>
2738
where
28-
S: Into<StatusCode>,
39+
S: TryInto<StatusCode>,
40+
S::Error: Debug,
2941
{
3042
self.map_err(|error| {
31-
let status = status.into();
43+
let status = status
44+
.try_into()
45+
.expect("Could not convert into a valid `StatusCode`");
3246
Error::new(status, error)
3347
})
3448
}
3549

50+
/// Wrap the error value with an additional status code that is evaluated
51+
/// lazily only once an error does occur.
52+
///
53+
/// # Panics
54+
///
55+
/// Panics if [`Status`][status] is not a valid [`StatusCode`][statuscode].
56+
///
57+
/// [status]: crate::Status
58+
/// [statuscode]: crate::StatusCode
3659
fn with_status<S, F>(self, f: F) -> Result<T, Error>
3760
where
38-
S: Into<StatusCode>,
61+
S: TryInto<StatusCode>,
62+
S::Error: Debug,
3963
F: FnOnce() -> S,
4064
{
4165
self.map_err(|error| {
42-
let status = f().into();
66+
let status = f()
67+
.try_into()
68+
.expect("Could not convert into a valid `StatusCode`");
4369
Error::new(status, error)
4470
})
4571
}
4672
}
4773

4874
impl<T> Status<T, Infallible> for Option<T> {
75+
/// Wrap the error value with an additional status code.
76+
///
77+
/// # Panics
78+
///
79+
/// Panics if [`Status`][status] is not a valid [`StatusCode`][statuscode].
80+
///
81+
/// [status]: crate::Status
82+
/// [statuscode]: crate::StatusCode
4983
fn status<S>(self, status: S) -> Result<T, Error>
5084
where
51-
S: Into<StatusCode>,
85+
S: TryInto<StatusCode>,
86+
S::Error: Debug,
5287
{
5388
self.ok_or_else(|| {
54-
let status = status.into();
89+
let status = status
90+
.try_into()
91+
.expect("Could not convert into a valid `StatusCode`");
5592
Error::from_str(status, "NoneError")
5693
})
5794
}
5895

96+
/// Wrap the error value with an additional status code that is evaluated
97+
/// lazily only once an error does occur.
98+
///
99+
/// # Panics
100+
///
101+
/// Panics if [`Status`][status] is not a valid [`StatusCode`][statuscode].
102+
///
103+
/// [status]: crate::Status
104+
/// [statuscode]: crate::StatusCode
59105
fn with_status<S, F>(self, f: F) -> Result<T, Error>
60106
where
61-
S: Into<StatusCode>,
107+
S: TryInto<StatusCode>,
108+
S::Error: Debug,
62109
F: FnOnce() -> S,
63110
{
64111
self.ok_or_else(|| {
65-
let status = f().into();
112+
let status = f()
113+
.try_into()
114+
.expect("Could not convert into a valid `StatusCode`");
66115
Error::from_str(status, "NoneError")
67116
})
68117
}
@@ -74,3 +123,21 @@ pub(crate) mod private {
74123
impl<T, E> Sealed for Result<T, E> {}
75124
impl<T> Sealed for Option<T> {}
76125
}
126+
127+
#[cfg(test)]
128+
mod test {
129+
use super::Status;
130+
131+
#[test]
132+
fn construct_shorthand_with_valid_status_code() {
133+
let _res = Some(()).status(200).unwrap();
134+
}
135+
136+
#[test]
137+
#[should_panic(expected = "Could not convert into a valid `StatusCode`")]
138+
fn construct_shorthand_with_invalid_status_code() {
139+
let res: Result<(), std::io::Error> =
140+
Err(std::io::Error::new(std::io::ErrorKind::Other, "oh no!"));
141+
let _res = res.status(600).unwrap();
142+
}
143+
}

0 commit comments

Comments
 (0)