@@ -50,8 +50,9 @@ use std::error::Error;
50
50
use std:: time:: Duration ;
51
51
use std:: path:: PathBuf ;
52
52
use iron:: prelude:: * ;
53
- use iron:: { self , Handler , status} ;
54
- use iron:: headers:: { CacheControl , CacheDirective , ContentType } ;
53
+ use iron:: { self , Handler , Url , status} ;
54
+ use iron:: headers:: { Expires , HttpDate , CacheControl , CacheDirective , ContentType } ;
55
+ use iron:: modifiers:: Redirect ;
55
56
use router:: { Router , NoRoute } ;
56
57
use staticfile:: Static ;
57
58
use handlebars_iron:: { HandlebarsEngine , DirectorySource } ;
@@ -258,9 +259,34 @@ impl Handler for CratesfyiHandler {
258
259
}
259
260
}
260
261
262
+ /// Represents the possible results of attempting to load a version requirement.
263
+ enum MatchVersion {
264
+ /// `match_version` was given an exact version, which matched a saved crate version.
265
+ Exact ( String ) ,
266
+ /// `match_version` was given a semver version requirement, which matched the given saved crate
267
+ /// version.
268
+ Semver ( String ) ,
269
+ /// `match_version` was given a version requirement which did not match any saved crate
270
+ /// versions.
271
+ None ,
272
+ }
261
273
274
+ impl MatchVersion {
275
+ /// Convert this `MatchVersion` into an `Option`, discarding whether the matched version came
276
+ /// from an exact version number or a semver requirement.
277
+ pub fn into_option ( self ) -> Option < String > {
278
+ match self {
279
+ MatchVersion :: Exact ( v) | MatchVersion :: Semver ( v) => Some ( v) ,
280
+ MatchVersion :: None => None ,
281
+ }
282
+ }
283
+ }
262
284
263
- fn match_version ( conn : & Connection , name : & str , version : Option < & str > ) -> Option < String > {
285
+ /// Checks the database for crate releases that match the given name and version.
286
+ ///
287
+ /// `version` may be an exact version number or loose semver version requirement. The return value
288
+ /// will indicate whether the given version exactly matched a version number from the database.
289
+ fn match_version ( conn : & Connection , name : & str , version : Option < & str > ) -> MatchVersion {
264
290
265
291
// version is an Option<&str> from router::Router::get
266
292
// need to decode first
@@ -278,7 +304,7 @@ fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option
278
304
let mut versions = Vec :: new ( ) ;
279
305
let rows = conn. query ( "SELECT versions FROM crates WHERE name = $1" , & [ & name] ) . unwrap ( ) ;
280
306
if rows. len ( ) == 0 {
281
- return None ;
307
+ return MatchVersion :: None ;
282
308
}
283
309
let versions_json: Json = rows. get ( 0 ) . get ( 0 ) ;
284
310
for version in versions_json. as_array ( ) . unwrap ( ) {
@@ -293,14 +319,14 @@ fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option
293
319
// we can't expect users to use semver in query
294
320
for version in & versions {
295
321
if version == & req_version {
296
- return Some ( version. clone ( ) ) ;
322
+ return MatchVersion :: Exact ( version. clone ( ) ) ;
297
323
}
298
324
}
299
325
300
326
// Now try to match with semver
301
327
let req_sem_ver = match VersionReq :: parse ( & req_version) {
302
328
Ok ( v) => v,
303
- Err ( _) => return None ,
329
+ Err ( _) => return MatchVersion :: None ,
304
330
} ;
305
331
306
332
// we need to sort versions first
@@ -312,7 +338,7 @@ fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option
312
338
// but check result just in case
313
339
let version = match Version :: parse ( & version) {
314
340
Ok ( v) => v,
315
- Err ( _) => return None ,
341
+ Err ( _) => return MatchVersion :: None ,
316
342
} ;
317
343
versions_sem. push ( version) ;
318
344
}
@@ -325,16 +351,16 @@ fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option
325
351
// semver is acting weird for '*' (any) range if a crate only have pre-release versions
326
352
// return first version if requested version is '*'
327
353
if req_version == "*" && !versions_sem. is_empty ( ) {
328
- return Some ( format ! ( "{}" , versions_sem[ 0 ] ) ) ;
354
+ return MatchVersion :: Semver ( format ! ( "{}" , versions_sem[ 0 ] ) ) ;
329
355
}
330
356
331
357
for version in & versions_sem {
332
358
if req_sem_ver. matches ( & version) {
333
- return Some ( format ! ( "{}" , version) ) ;
359
+ return MatchVersion :: Semver ( format ! ( "{}" , version) ) ;
334
360
}
335
361
}
336
362
337
- None
363
+ MatchVersion :: None
338
364
}
339
365
340
366
@@ -431,7 +457,14 @@ fn duration_to_str(ts: time::Timespec) -> String {
431
457
432
458
}
433
459
460
+ /// Creates a `Response` which redirects to the given path on the scheme/host/port from the given
461
+ /// `Request`.
462
+ fn redirect ( url : Url ) -> Response {
463
+ let mut resp = Response :: with ( ( status:: Found , Redirect ( url) ) ) ;
464
+ resp. headers . set ( Expires ( HttpDate ( time:: now ( ) ) ) ) ;
434
465
466
+ resp
467
+ }
435
468
436
469
fn style_css_handler ( _: & mut Request ) -> IronResult < Response > {
437
470
let mut response = Response :: with ( ( status:: Ok , STYLE_CSS ) ) ;
0 commit comments