@@ -50,8 +50,9 @@ use std::error::Error;
5050use std:: time:: Duration ;
5151use std:: path:: PathBuf ;
5252use 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 ;
5556use router:: { Router , NoRoute } ;
5657use staticfile:: Static ;
5758use handlebars_iron:: { HandlebarsEngine , DirectorySource } ;
@@ -258,9 +259,34 @@ impl Handler for CratesfyiHandler {
258259 }
259260}
260261
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+ }
261273
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+ }
262284
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 {
264290
265291 // version is an Option<&str> from router::Router::get
266292 // need to decode first
@@ -278,7 +304,7 @@ fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option
278304 let mut versions = Vec :: new ( ) ;
279305 let rows = conn. query ( "SELECT versions FROM crates WHERE name = $1" , & [ & name] ) . unwrap ( ) ;
280306 if rows. len ( ) == 0 {
281- return None ;
307+ return MatchVersion :: None ;
282308 }
283309 let versions_json: Json = rows. get ( 0 ) . get ( 0 ) ;
284310 for version in versions_json. as_array ( ) . unwrap ( ) {
@@ -293,14 +319,14 @@ fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option
293319 // we can't expect users to use semver in query
294320 for version in & versions {
295321 if version == & req_version {
296- return Some ( version. clone ( ) ) ;
322+ return MatchVersion :: Exact ( version. clone ( ) ) ;
297323 }
298324 }
299325
300326 // Now try to match with semver
301327 let req_sem_ver = match VersionReq :: parse ( & req_version) {
302328 Ok ( v) => v,
303- Err ( _) => return None ,
329+ Err ( _) => return MatchVersion :: None ,
304330 } ;
305331
306332 // we need to sort versions first
@@ -312,7 +338,7 @@ fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option
312338 // but check result just in case
313339 let version = match Version :: parse ( & version) {
314340 Ok ( v) => v,
315- Err ( _) => return None ,
341+ Err ( _) => return MatchVersion :: None ,
316342 } ;
317343 versions_sem. push ( version) ;
318344 }
@@ -325,16 +351,16 @@ fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option
325351 // semver is acting weird for '*' (any) range if a crate only have pre-release versions
326352 // return first version if requested version is '*'
327353 if req_version == "*" && !versions_sem. is_empty ( ) {
328- return Some ( format ! ( "{}" , versions_sem[ 0 ] ) ) ;
354+ return MatchVersion :: Semver ( format ! ( "{}" , versions_sem[ 0 ] ) ) ;
329355 }
330356
331357 for version in & versions_sem {
332358 if req_sem_ver. matches ( & version) {
333- return Some ( format ! ( "{}" , version) ) ;
359+ return MatchVersion :: Semver ( format ! ( "{}" , version) ) ;
334360 }
335361 }
336362
337- None
363+ MatchVersion :: None
338364}
339365
340366
@@ -431,7 +457,14 @@ fn duration_to_str(ts: time::Timespec) -> String {
431457
432458}
433459
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 ( ) ) ) ) ;
434465
466+ resp
467+ }
435468
436469fn style_css_handler ( _: & mut Request ) -> IronResult < Response > {
437470 let mut response = Response :: with ( ( status:: Ok , STYLE_CSS ) ) ;
0 commit comments