@@ -7,7 +7,6 @@ use std::io::prelude::*;
7
7
use std:: io:: { Cursor , SeekFrom } ;
8
8
use std:: time:: Instant ;
9
9
10
- use anyhow:: { format_err, Context } ;
11
10
use curl:: easy:: { Easy , List } ;
12
11
use http_auth:: ChallengeParser ;
13
12
use percent_encoding:: { percent_encode, NON_ALPHANUMERIC } ;
@@ -140,37 +139,66 @@ struct Crates {
140
139
meta : TotalCrates ,
141
140
}
142
141
142
+ /// Error returned when interacting with a registry.
143
143
#[ derive( Debug ) ]
144
144
pub enum Error {
145
+ /// Error from libcurl.
145
146
Curl ( curl:: Error ) ,
147
+
148
+ /// Error from seriailzing the request payload and deserialzing the
149
+ /// response body (like response body didn't match expected structure).
150
+ Json ( serde_json:: Error ) ,
151
+
152
+ /// Error from IO. Mostly from reading the tarball to upload.
153
+ Io ( std:: io:: Error ) ,
154
+
155
+ /// Response body was not valid utf8.
156
+ Utf8 ( std:: string:: FromUtf8Error ) ,
157
+
158
+ /// Error from API response containing JSON field `errors.details`.
146
159
Api {
147
160
code : u32 ,
148
161
errors : Vec < String > ,
149
162
challenge : Option < Challenge > ,
150
163
} ,
164
+
165
+ /// Error from API response which didn't have pre-programmed `errors.details`.
151
166
Code {
152
167
code : u32 ,
153
168
headers : Vec < String > ,
154
169
body : String ,
155
170
} ,
156
- Other ( anyhow:: Error ) ,
171
+
172
+ /// Reason why the token was invalid.
173
+ InvalidToken ( & ' static str ) ,
174
+
175
+ /// Server was unavailable and timeouted. Happened when uploading a way
176
+ /// too large tarball to crates.io.
177
+ Timeout ( u64 ) ,
157
178
}
158
179
159
180
impl std:: error:: Error for Error {
160
181
fn source ( & self ) -> Option < & ( dyn std:: error:: Error + ' static ) > {
161
182
match self {
162
- Self :: Curl ( ..) => None ,
183
+ Self :: Curl ( e) => Some ( e) ,
184
+ Self :: Json ( e) => Some ( e) ,
185
+ Self :: Io ( e) => Some ( e) ,
186
+ Self :: Utf8 ( e) => Some ( e) ,
163
187
Self :: Api { .. } => None ,
164
188
Self :: Code { .. } => None ,
165
- Self :: Other ( e) => Some ( e. as_ref ( ) ) ,
189
+ Self :: InvalidToken ( ..) => None ,
190
+ Self :: Timeout ( ..) => None ,
166
191
}
167
192
}
168
193
}
169
194
170
195
impl fmt:: Display for Error {
171
196
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
172
197
match self {
173
- Self :: Curl ( e) => write ! ( f, "{}" , e) ,
198
+ Self :: Curl ( e) => write ! ( f, "{e}" ) ,
199
+ Self :: Json ( e) => write ! ( f, "{e}" ) ,
200
+ Self :: Io ( e) => write ! ( f, "{e}" ) ,
201
+ Self :: Utf8 ( e) => write ! ( f, "{e}" ) ,
174
202
Self :: Api { code, errors, .. } => {
175
203
f. write_str ( "the remote server responded with an error" ) ?;
176
204
if * code != 200 {
@@ -193,7 +221,14 @@ impl fmt::Display for Error {
193
221
headers. join( "\n \t " ) ,
194
222
body
195
223
) ,
196
- Self :: Other ( ..) => write ! ( f, "invalid response from server" ) ,
224
+ Self :: InvalidToken ( e) => write ! ( f, "{e}" ) ,
225
+ Self :: Timeout ( tarball_size) => write ! (
226
+ f,
227
+ "Request timed out after 30 seconds. If you're trying to \
228
+ upload a crate it may be too large. If the crate is under \
229
+ 10MB in size, you can email [email protected] for assistance.\n \
230
+ Total size was {tarball_size}."
231
+ ) ,
197
232
}
198
233
}
199
234
}
@@ -204,21 +239,21 @@ impl From<curl::Error> for Error {
204
239
}
205
240
}
206
241
207
- impl From < anyhow :: Error > for Error {
208
- fn from ( error : anyhow :: Error ) -> Self {
209
- Self :: Other ( error)
242
+ impl From < serde_json :: Error > for Error {
243
+ fn from ( error : serde_json :: Error ) -> Self {
244
+ Self :: Json ( error)
210
245
}
211
246
}
212
247
213
- impl From < serde_json :: Error > for Error {
214
- fn from ( error : serde_json :: Error ) -> Self {
215
- Self :: Other ( error. into ( ) )
248
+ impl From < std :: io :: Error > for Error {
249
+ fn from ( error : std :: io :: Error ) -> Self {
250
+ Self :: Io ( error)
216
251
}
217
252
}
218
253
219
- impl From < url :: ParseError > for Error {
220
- fn from ( error : url :: ParseError ) -> Self {
221
- Self :: Other ( error. into ( ) )
254
+ impl From < std :: string :: FromUtf8Error > for Error {
255
+ fn from ( error : std :: string :: FromUtf8Error ) -> Self {
256
+ Self :: Utf8 ( error)
222
257
}
223
258
}
224
259
@@ -255,12 +290,9 @@ impl Registry {
255
290
}
256
291
257
292
fn token ( & self ) -> Result < & str , Error > {
258
- let token = match self . token . as_ref ( ) {
259
- Some ( s) => s,
260
- None => {
261
- return Err ( format_err ! ( "no upload token found, please run `cargo login`" ) . into ( ) )
262
- }
263
- } ;
293
+ let token = self . token . as_ref ( ) . ok_or_else ( || {
294
+ Error :: InvalidToken ( "no upload token found, please run `cargo login`" )
295
+ } ) ?;
264
296
check_token ( token) ?;
265
297
Ok ( token)
266
298
}
@@ -306,12 +338,8 @@ impl Registry {
306
338
// This checks the length using seeking instead of metadata, because
307
339
// on some filesystems, getting the metadata will fail because
308
340
// the file was renamed in ops::package.
309
- let tarball_len = tarball
310
- . seek ( SeekFrom :: End ( 0 ) )
311
- . with_context ( || "failed to seek tarball" ) ?;
312
- tarball
313
- . seek ( SeekFrom :: Start ( 0 ) )
314
- . with_context ( || "failed to seek tarball" ) ?;
341
+ let tarball_len = tarball. seek ( SeekFrom :: End ( 0 ) ) ?;
342
+ tarball. seek ( SeekFrom :: Start ( 0 ) ) ?;
315
343
let header = {
316
344
let mut w = Vec :: new ( ) ;
317
345
w. extend ( & ( json. len ( ) as u32 ) . to_le_bytes ( ) ) ;
@@ -341,13 +369,7 @@ impl Registry {
341
369
&& started. elapsed ( ) . as_secs ( ) >= 29
342
370
&& self . host_is_crates_io ( ) =>
343
371
{
344
- format_err ! (
345
- "Request timed out after 30 seconds. If you're trying to \
346
- upload a crate it may be too large. If the crate is under \
347
- 10MB in size, you can email [email protected] for assistance.\n \
348
- Total size was {}.",
349
- tarball_len
350
- )
372
+ Error :: Timeout ( tarball_len)
351
373
}
352
374
_ => e. into ( ) ,
353
375
} ) ?;
@@ -470,14 +492,7 @@ impl Registry {
470
492
handle. perform ( ) ?;
471
493
}
472
494
473
- let body = match String :: from_utf8 ( body) {
474
- Ok ( body) => body,
475
- Err ( ..) => {
476
- return Err ( Error :: Other ( format_err ! (
477
- "response body was not valid utf-8"
478
- ) ) )
479
- }
480
- } ;
495
+ let body = String :: from_utf8 ( body) ?;
481
496
let errors = serde_json:: from_str :: < ApiErrorList > ( & body)
482
497
. ok ( )
483
498
. map ( |s| s. errors . into_iter ( ) . map ( |s| s. detail ) . collect :: < Vec < _ > > ( ) ) ;
@@ -623,7 +638,7 @@ pub fn is_url_crates_io(url: &str) -> bool {
623
638
/// registries only create tokens in that format so that is as less restricted as possible.
624
639
pub fn check_token ( token : & str ) -> Result < ( ) , Error > {
625
640
if token. is_empty ( ) {
626
- return Err ( format_err ! ( "please provide a non-empty token" ) . into ( ) ) ;
641
+ return Err ( Error :: InvalidToken ( "please provide a non-empty token" ) ) ;
627
642
}
628
643
if token. bytes ( ) . all ( |b| {
629
644
// This is essentially the US-ASCII limitation of
@@ -634,11 +649,10 @@ pub fn check_token(token: &str) -> Result<(), Error> {
634
649
} ) {
635
650
Ok ( ( ) )
636
651
} else {
637
- Err ( format_err ! (
652
+ Err ( Error :: InvalidToken (
638
653
"token contains invalid characters.\n Only printable ISO-8859-1 characters \
639
- are allowed as it is sent in a HTTPS header."
640
- )
641
- . into ( ) )
654
+ are allowed as it is sent in a HTTPS header.",
655
+ ) )
642
656
}
643
657
}
644
658
0 commit comments