@@ -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 percent_encoding:: { percent_encode, NON_ALPHANUMERIC } ;
13
12
use serde:: { Deserialize , Serialize } ;
@@ -127,37 +126,66 @@ struct Crates {
127
126
meta : TotalCrates ,
128
127
}
129
128
129
+ /// Error returned when interacting with a registry.
130
130
#[ derive( Debug ) ]
131
131
pub enum Error {
132
+ /// Error from libcurl.
132
133
Curl ( curl:: Error ) ,
134
+
135
+ /// Error from seriailzing the request payload and deserialzing the
136
+ /// response body (like response body didn't match expected structure).
137
+ Json ( serde_json:: Error ) ,
138
+
139
+ /// Error from IO. Mostly from reading the tarball to upload.
140
+ Io ( std:: io:: Error ) ,
141
+
142
+ /// Response body was not valid utf8.
143
+ Utf8 ( std:: string:: FromUtf8Error ) ,
144
+
145
+ /// Error from API response containing JSON field `errors.details`.
133
146
Api {
134
147
code : u32 ,
135
148
headers : Vec < String > ,
136
149
errors : Vec < String > ,
137
150
} ,
151
+
152
+ /// Error from API response which didn't have pre-programmed `errors.details`.
138
153
Code {
139
154
code : u32 ,
140
155
headers : Vec < String > ,
141
156
body : String ,
142
157
} ,
143
- Other ( anyhow:: Error ) ,
158
+
159
+ /// Reason why the token was invalid.
160
+ InvalidToken ( & ' static str ) ,
161
+
162
+ /// Server was unavailable and timeouted. Happened when uploading a way
163
+ /// too large tarball to crates.io.
164
+ Timeout ( u64 ) ,
144
165
}
145
166
146
167
impl std:: error:: Error for Error {
147
168
fn source ( & self ) -> Option < & ( dyn std:: error:: Error + ' static ) > {
148
169
match self {
149
- Self :: Curl ( ..) => None ,
170
+ Self :: Curl ( e) => Some ( e) ,
171
+ Self :: Json ( e) => Some ( e) ,
172
+ Self :: Io ( e) => Some ( e) ,
173
+ Self :: Utf8 ( e) => Some ( e) ,
150
174
Self :: Api { .. } => None ,
151
175
Self :: Code { .. } => None ,
152
- Self :: Other ( e) => Some ( e. as_ref ( ) ) ,
176
+ Self :: InvalidToken ( ..) => None ,
177
+ Self :: Timeout ( ..) => None ,
153
178
}
154
179
}
155
180
}
156
181
157
182
impl fmt:: Display for Error {
158
183
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
159
184
match self {
160
- Self :: Curl ( e) => write ! ( f, "{}" , e) ,
185
+ Self :: Curl ( e) => write ! ( f, "{e}" ) ,
186
+ Self :: Json ( e) => write ! ( f, "{e}" ) ,
187
+ Self :: Io ( e) => write ! ( f, "{e}" ) ,
188
+ Self :: Utf8 ( e) => write ! ( f, "{e}" ) ,
161
189
Self :: Api { code, errors, .. } => {
162
190
f. write_str ( "the remote server responded with an error" ) ?;
163
191
if * code != 200 {
@@ -180,7 +208,14 @@ impl fmt::Display for Error {
180
208
headers. join( "\n \t " ) ,
181
209
body
182
210
) ,
183
- Self :: Other ( ..) => write ! ( f, "invalid response from server" ) ,
211
+ Self :: InvalidToken ( e) => write ! ( f, "{e}" ) ,
212
+ Self :: Timeout ( tarball_size) => write ! (
213
+ f,
214
+ "Request timed out after 30 seconds. If you're trying to \
215
+ upload a crate it may be too large. If the crate is under \
216
+ 10MB in size, you can email [email protected] for assistance.\n \
217
+ Total size was {tarball_size}."
218
+ ) ,
184
219
}
185
220
}
186
221
}
@@ -191,21 +226,21 @@ impl From<curl::Error> for Error {
191
226
}
192
227
}
193
228
194
- impl From < anyhow :: Error > for Error {
195
- fn from ( error : anyhow :: Error ) -> Self {
196
- Self :: Other ( error)
229
+ impl From < serde_json :: Error > for Error {
230
+ fn from ( error : serde_json :: Error ) -> Self {
231
+ Self :: Json ( error)
197
232
}
198
233
}
199
234
200
- impl From < serde_json :: Error > for Error {
201
- fn from ( error : serde_json :: Error ) -> Self {
202
- Self :: Other ( error. into ( ) )
235
+ impl From < std :: io :: Error > for Error {
236
+ fn from ( error : std :: io :: Error ) -> Self {
237
+ Self :: Io ( error)
203
238
}
204
239
}
205
240
206
- impl From < url :: ParseError > for Error {
207
- fn from ( error : url :: ParseError ) -> Self {
208
- Self :: Other ( error. into ( ) )
241
+ impl From < std :: string :: FromUtf8Error > for Error {
242
+ fn from ( error : std :: string :: FromUtf8Error ) -> Self {
243
+ Self :: Utf8 ( error)
209
244
}
210
245
}
211
246
@@ -242,12 +277,9 @@ impl Registry {
242
277
}
243
278
244
279
fn token ( & self ) -> Result < & str > {
245
- let token = match self . token . as_ref ( ) {
246
- Some ( s) => s,
247
- None => {
248
- return Err ( format_err ! ( "no upload token found, please run `cargo login`" ) . into ( ) )
249
- }
250
- } ;
280
+ let token = self . token . as_ref ( ) . ok_or_else ( || {
281
+ Error :: InvalidToken ( "no upload token found, please run `cargo login`" )
282
+ } ) ?;
251
283
check_token ( token) ?;
252
284
Ok ( token)
253
285
}
@@ -293,12 +325,8 @@ impl Registry {
293
325
// This checks the length using seeking instead of metadata, because
294
326
// on some filesystems, getting the metadata will fail because
295
327
// the file was renamed in ops::package.
296
- let tarball_len = tarball
297
- . seek ( SeekFrom :: End ( 0 ) )
298
- . with_context ( || "failed to seek tarball" ) ?;
299
- tarball
300
- . seek ( SeekFrom :: Start ( 0 ) )
301
- . with_context ( || "failed to seek tarball" ) ?;
328
+ let tarball_len = tarball. seek ( SeekFrom :: End ( 0 ) ) ?;
329
+ tarball. seek ( SeekFrom :: Start ( 0 ) ) ?;
302
330
let header = {
303
331
let mut w = Vec :: new ( ) ;
304
332
w. extend ( & ( json. len ( ) as u32 ) . to_le_bytes ( ) ) ;
@@ -328,13 +356,7 @@ impl Registry {
328
356
&& started. elapsed ( ) . as_secs ( ) >= 29
329
357
&& self . host_is_crates_io ( ) =>
330
358
{
331
- format_err ! (
332
- "Request timed out after 30 seconds. If you're trying to \
333
- upload a crate it may be too large. If the crate is under \
334
- 10MB in size, you can email [email protected] for assistance.\n \
335
- Total size was {}.",
336
- tarball_len
337
- )
359
+ Error :: Timeout ( tarball_len)
338
360
}
339
361
_ => e. into ( ) ,
340
362
} ) ?;
@@ -457,14 +479,7 @@ impl Registry {
457
479
handle. perform ( ) ?;
458
480
}
459
481
460
- let body = match String :: from_utf8 ( body) {
461
- Ok ( body) => body,
462
- Err ( ..) => {
463
- return Err ( Error :: Other ( format_err ! (
464
- "response body was not valid utf-8"
465
- ) ) )
466
- }
467
- } ;
482
+ let body = String :: from_utf8 ( body) ?;
468
483
let errors = serde_json:: from_str :: < ApiErrorList > ( & body)
469
484
. ok ( )
470
485
. map ( |s| s. errors . into_iter ( ) . map ( |s| s. detail ) . collect :: < Vec < _ > > ( ) ) ;
@@ -548,7 +563,7 @@ pub fn is_url_crates_io(url: &str) -> bool {
548
563
/// registries only create tokens in that format so that is as less restricted as possible.
549
564
pub fn check_token ( token : & str ) -> Result < ( ) > {
550
565
if token. is_empty ( ) {
551
- return Err ( format_err ! ( "please provide a non-empty token" ) . into ( ) ) ;
566
+ return Err ( Error :: InvalidToken ( "please provide a non-empty token" ) ) ;
552
567
}
553
568
if token. bytes ( ) . all ( |b| {
554
569
// This is essentially the US-ASCII limitation of
@@ -559,10 +574,9 @@ pub fn check_token(token: &str) -> Result<()> {
559
574
} ) {
560
575
Ok ( ( ) )
561
576
} else {
562
- Err ( format_err ! (
577
+ Err ( Error :: InvalidToken (
563
578
"token contains invalid characters.\n Only printable ISO-8859-1 characters \
564
- are allowed as it is sent in a HTTPS header."
565
- )
566
- . into ( ) )
579
+ are allowed as it is sent in a HTTPS header.",
580
+ ) )
567
581
}
568
582
}
0 commit comments