Skip to content

Commit 274f601

Browse files
committed
Auto merge of #3901 - Turbo87:chain-error, r=hi-rustin
Remove `ChainError` trait The `chain_error()` function can be replaced by `ok_or_else()` (for `Option`) or `map_err()` (for `Result`), which makes the intent a little clearer, and removes one of the layers of indirection from our error handling, without being significantly more verbose. Please note that this only removes the `ChainError` **trait**, but the `ChainedError` **struct** still exists.
2 parents ad61659 + 9e40742 commit 274f601

File tree

11 files changed

+29
-63
lines changed

11 files changed

+29
-63
lines changed

src/controllers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ mod prelude {
1919

2020
pub use crate::db::RequestTransaction;
2121
pub use crate::middleware::app::RequestApp;
22-
pub use crate::util::errors::{cargo_err, AppError, AppResult, ChainError}; // TODO: Remove cargo_err from here
22+
pub use crate::util::errors::{cargo_err, AppError, AppResult}; // TODO: Remove cargo_err from here
2323
pub use crate::util::{AppResponse, EndpointResult};
2424

2525
use indexmap::IndexMap;

src/controllers/krate/publish.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ pub fn publish(req: &mut dyn RequestExt) -> EndpointResult {
130130

131131
let content_length = req
132132
.content_length()
133-
.chain_error(|| cargo_err("missing header: Content-Length"))?;
133+
.ok_or_else(|| cargo_err("missing header: Content-Length"))?;
134134

135135
let maximums = Maximums::new(
136136
krate.max_upload_size,

src/controllers/krate/search.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::models::{
1010
Crate, CrateBadge, CrateOwner, CrateVersions, OwnerKind, TopVersions, Version,
1111
};
1212
use crate::schema::*;
13-
use crate::util::errors::{bad_request, ChainError};
13+
use crate::util::errors::bad_request;
1414
use crate::views::EncodableCrate;
1515

1616
use crate::controllers::helpers::pagination::{Page, Paginated, PaginationOptions};
@@ -153,7 +153,7 @@ pub fn search(req: &mut dyn RequestExt) -> EndpointResult {
153153
letter
154154
.chars()
155155
.next()
156-
.chain_error(|| bad_request("letter value must contain 1 character"))?
156+
.ok_or_else(|| bad_request("letter value must contain 1 character"))?
157157
.to_lowercase()
158158
.collect::<String>()
159159
);

src/controllers/token.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub fn new(req: &mut dyn RequestExt) -> EndpointResult {
3939
let max_size = 2000;
4040
let length = req
4141
.content_length()
42-
.chain_error(|| bad_request("missing header: Content-Length"))?;
42+
.ok_or_else(|| bad_request("missing header: Content-Length"))?;
4343

4444
if length > max_size {
4545
return Err(bad_request(&format!("max content length is: {}", max_size)));

src/controllers/user/me.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ pub fn regenerate_token_and_send(req: &mut dyn RequestExt) -> EndpointResult {
187187

188188
let param_user_id = req.params()["user_id"]
189189
.parse::<i32>()
190-
.chain_error(|| bad_request("invalid user_id"))?;
190+
.map_err(|err| err.chain(bad_request("invalid user_id")))?;
191191
let authenticated_user = req.authenticate()?;
192192
let conn = req.db_conn()?;
193193
let user = authenticated_user.user();

src/controllers/user/other.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use crate::controllers::frontend_prelude::*;
22

33
use crate::models::{CrateOwner, OwnerKind, User};
44
use crate::schema::{crate_owners, crates, users};
5-
use crate::util::errors::ChainError;
65
use crate::views::EncodablePublicUser;
76

87
/// Handles the `GET /users/:user_id` route.
@@ -25,7 +24,7 @@ pub fn stats(req: &mut dyn RequestExt) -> EndpointResult {
2524

2625
let user_id = &req.params()["user_id"]
2726
.parse::<i32>()
28-
.chain_error(|| bad_request("invalid user_id"))?;
27+
.map_err(|err| err.chain(bad_request("invalid user_id")))?;
2928
let conn = req.db_conn()?;
3029

3130
let data: i64 = CrateOwner::by_owner_kind(OwnerKind::User)

src/controllers/user/session.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub fn authorize(req: &mut dyn RequestExt) -> EndpointResult {
9090
.github_oauth
9191
.exchange_code(code)
9292
.request(http_client)
93-
.chain_error(|| server_error("Error obtaining token"))?;
93+
.map_err(|err| err.chain(server_error("Error obtaining token")))?;
9494
let token = token.access_token();
9595

9696
// Fetch the user info from GitHub using the access token we just got and create a user record

src/controllers/util.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use super::prelude::*;
66
use crate::middleware::log_request;
77
use crate::models::{ApiToken, User};
88
use crate::util::errors::{
9-
account_locked, forbidden, internal, AppError, AppResult, ChainError,
10-
InsecurelyGeneratedTokenRevoked,
9+
account_locked, forbidden, internal, AppError, AppResult, InsecurelyGeneratedTokenRevoked,
1110
};
1211

1312
#[derive(Debug)]
@@ -62,8 +61,7 @@ fn verify_origin(req: &dyn RequestExt) -> AppResult<()> {
6261
"only same-origin requests can be authenticated. got {:?}",
6362
bad_origin
6463
);
65-
return Err(internal(&error_message))
66-
.chain_error(|| Box::new(forbidden()) as Box<dyn AppError>);
64+
return Err(internal(&error_message).chain(forbidden()));
6765
}
6866
Ok(())
6967
}
@@ -76,7 +74,7 @@ fn authenticate_user(req: &dyn RequestExt) -> AppResult<AuthenticatedUser> {
7674

7775
if let Some(id) = user_id_from_session {
7876
let user = User::find(&conn, id)
79-
.chain_error(|| internal("user_id from cookie not found in database"))?;
77+
.map_err(|err| err.chain(internal("user_id from cookie not found in database")))?;
8078

8179
return Ok(AuthenticatedUser {
8280
user,
@@ -100,7 +98,7 @@ fn authenticate_user(req: &dyn RequestExt) -> AppResult<AuthenticatedUser> {
10098
})?;
10199

102100
let user = User::find(&conn, token.user_id)
103-
.chain_error(|| internal("user_id from token not found in database"))?;
101+
.map_err(|err| err.chain(internal("user_id from token not found in database")))?;
104102

105103
return Ok(AuthenticatedUser {
106104
user,
@@ -109,7 +107,7 @@ fn authenticate_user(req: &dyn RequestExt) -> AppResult<AuthenticatedUser> {
109107
}
110108

111109
// Unable to authenticate the user
112-
return Err(internal("no cookie session or auth header found")).chain_error(forbidden);
110+
return Err(internal("no cookie session or auth header found").chain(forbidden()));
113111
}
114112

115113
impl<'a> UserAuthenticationExt for dyn RequestExt + 'a {

src/router.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,7 @@ impl<H: Handler> Handler for R<H> {
179179
#[cfg(test)]
180180
mod tests {
181181
use super::*;
182-
use crate::util::errors::{
183-
bad_request, cargo_err, forbidden, internal, not_found, AppError, ChainError,
184-
};
182+
use crate::util::errors::{bad_request, cargo_err, forbidden, internal, not_found, AppError};
185183
use crate::util::EndpointResult;
186184

187185
use conduit::StatusCode;
@@ -227,8 +225,8 @@ mod tests {
227225
let response = C(|_| {
228226
Err("-1"
229227
.parse::<u8>()
230-
.chain_error(|| internal("middle error"))
231-
.chain_error(|| bad_request("outer user facing error"))
228+
.map_err(|err| err.chain(internal("middle error")))
229+
.map_err(|err| err.chain(bad_request("outer user facing error")))
232230
.unwrap_err())
233231
})
234232
.call(&mut req)

src/uploaders.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use flate2::read::GzDecoder;
44
use reqwest::{blocking::Client, header};
55
use sha2::{Digest, Sha256};
66

7-
use crate::util::errors::{cargo_err, internal, AppResult, ChainError};
7+
use crate::util::errors::{cargo_err, internal, AppError, AppResult};
88
use crate::util::{LimitErrorReader, Maximums};
99

1010
use std::env;
@@ -200,8 +200,10 @@ fn verify_tarball(
200200
let mut archive = tar::Archive::new(decoder);
201201
let prefix = format!("{}-{}", krate.name, vers);
202202
for entry in archive.entries()? {
203-
let entry = entry.chain_error(|| {
204-
cargo_err("uploaded tarball is malformed or too large when decompressed")
203+
let entry = entry.map_err(|err| {
204+
err.chain(cargo_err(
205+
"uploaded tarball is malformed or too large when decompressed",
206+
))
205207
})?;
206208

207209
// Verify that all entries actually start with `$name-$vers/`.

src/util/errors.rs

Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -152,42 +152,12 @@ pub type AppResult<T> = Result<T, Box<dyn AppError>>;
152152
// =============================================================================
153153
// Chaining errors
154154

155-
pub trait ChainError<T> {
156-
fn chain_error<E, F>(self, callback: F) -> AppResult<T>
157-
where
158-
E: AppError,
159-
F: FnOnce() -> E;
160-
}
161-
162155
#[derive(Debug)]
163156
struct ChainedError<E> {
164157
error: E,
165158
cause: Box<dyn AppError>,
166159
}
167160

168-
impl<T, E: AppError> ChainError<T> for Result<T, E> {
169-
fn chain_error<E2, C>(self, callback: C) -> AppResult<T>
170-
where
171-
E2: AppError,
172-
C: FnOnce() -> E2,
173-
{
174-
self.map_err(move |err| err.chain(callback()))
175-
}
176-
}
177-
178-
impl<T> ChainError<T> for Option<T> {
179-
fn chain_error<E, C>(self, callback: C) -> AppResult<T>
180-
where
181-
E: AppError,
182-
C: FnOnce() -> E,
183-
{
184-
match self {
185-
Some(t) => Ok(t),
186-
None => Err(Box::new(callback())),
187-
}
188-
}
189-
}
190-
191161
impl<E: AppError> AppError for ChainedError<E> {
192162
fn response(&self) -> Option<AppResponse> {
193163
self.error.response()
@@ -274,17 +244,16 @@ pub(crate) fn std_error(e: Box<dyn AppError>) -> Box<dyn Error + Send> {
274244
#[test]
275245
fn chain_error_internal() {
276246
assert_eq!(
277-
None::<()>
278-
.chain_error(|| internal("inner"))
279-
.chain_error(|| internal("middle"))
280-
.chain_error(|| internal("outer"))
247+
Err::<(), _>(internal("inner"))
248+
.map_err(|err| err.chain(internal("middle")))
249+
.map_err(|err| err.chain(internal("outer")))
281250
.unwrap_err()
282251
.to_string(),
283252
"outer caused by middle caused by inner"
284253
);
285254
assert_eq!(
286255
Err::<(), _>(internal("inner"))
287-
.chain_error(|| internal("outer"))
256+
.map_err(|err| err.chain(internal("outer")))
288257
.unwrap_err()
289258
.to_string(),
290259
"outer caused by inner"
@@ -293,14 +262,14 @@ fn chain_error_internal() {
293262
// Don't do this, the user will see a generic 500 error instead of the intended message
294263
assert_eq!(
295264
Err::<(), _>(cargo_err("inner"))
296-
.chain_error(|| internal("outer"))
265+
.map_err(|err| err.chain(internal("outer")))
297266
.unwrap_err()
298267
.to_string(),
299268
"outer caused by inner"
300269
);
301270
assert_eq!(
302271
Err::<(), _>(forbidden())
303-
.chain_error(|| internal("outer"))
272+
.map_err(|err| err.chain(internal("outer")))
304273
.unwrap_err()
305274
.to_string(),
306275
"outer caused by must be logged in to perform that action"
@@ -312,7 +281,7 @@ fn chain_error_user_facing() {
312281
// Do this rarely, the user will only see the outer error
313282
assert_eq!(
314283
Err::<(), _>(cargo_err("inner"))
315-
.chain_error(|| cargo_err("outer"))
284+
.map_err(|err| err.chain(cargo_err("outer")))
316285
.unwrap_err()
317286
.to_string(),
318287
"outer caused by inner" // never logged
@@ -322,7 +291,7 @@ fn chain_error_user_facing() {
322291
// The inner error never bubbles up to the logging middleware
323292
assert_eq!(
324293
Err::<(), _>(std::io::Error::from(std::io::ErrorKind::PermissionDenied))
325-
.chain_error(|| cargo_err("outer"))
294+
.map_err(|err| err.chain(cargo_err("outer")))
326295
.unwrap_err()
327296
.to_string(),
328297
"outer caused by permission denied" // never logged

0 commit comments

Comments
 (0)