77use semver:: { Comparator , Op , Prerelease , Version , VersionReq } ;
88
99pub ( crate ) fn matches_prerelease ( req : & VersionReq , ver : & Version ) -> bool {
10+ // Whether there are pre release version can be as lower bound
11+ let lower_bound_prerelease = & req. comparators . iter ( ) . any ( |cmp| {
12+ if matches ! ( cmp. op, Op :: Greater | Op :: GreaterEq ) && !cmp. pre . is_empty ( ) {
13+ true
14+ } else {
15+ false
16+ }
17+ } ) ;
1018 for cmp in & req. comparators {
11- if !matches_prerelease_impl ( cmp, ver) {
19+ if !matches_prerelease_impl ( cmp, ver, lower_bound_prerelease ) {
1220 return false ;
1321 }
1422 }
1523
1624 true
1725}
1826
19- fn matches_prerelease_impl ( cmp : & Comparator , ver : & Version ) -> bool {
27+ fn matches_prerelease_impl ( cmp : & Comparator , ver : & Version , lower_bound_prerelease : & bool ) -> bool {
2028 match cmp. op {
2129 Op :: Exact | Op :: Wildcard => matches_exact_prerelease ( cmp, ver) ,
2230 Op :: Greater => matches_greater ( cmp, ver) ,
@@ -26,7 +34,13 @@ fn matches_prerelease_impl(cmp: &Comparator, ver: &Version) -> bool {
2634 }
2735 matches_greater ( cmp, ver)
2836 }
29- Op :: Less => matches_less ( & fill_partial_req ( cmp) , ver) ,
37+ Op :: Less => {
38+ if * lower_bound_prerelease {
39+ matches_less ( & fill_partial_req ( cmp) , ver)
40+ } else {
41+ matches_less ( & fill_partial_req_include_pre ( cmp) , ver)
42+ }
43+ }
3044 Op :: LessEq => {
3145 if matches_exact_prerelease ( cmp, ver) {
3246 return true ;
@@ -125,6 +139,21 @@ fn fill_partial_req(cmp: &Comparator) -> Comparator {
125139 cmp
126140}
127141
142+ fn fill_partial_req_include_pre ( cmp : & Comparator ) -> Comparator {
143+ let mut cmp = cmp. clone ( ) ;
144+ if cmp. minor . is_none ( ) {
145+ cmp. minor = Some ( 0 ) ;
146+ cmp. patch = Some ( 0 ) ;
147+ cmp. pre = Prerelease :: new ( "0" ) . unwrap ( ) ;
148+ } else if cmp. patch . is_none ( ) {
149+ cmp. patch = Some ( 0 ) ;
150+ }
151+ if cmp. pre . is_empty ( ) {
152+ cmp. pre = Prerelease :: new ( "0" ) . unwrap ( ) ;
153+ }
154+ cmp
155+ }
156+
128157fn matches_exact_prerelease ( cmp : & Comparator , ver : & Version ) -> bool {
129158 if matches_exact ( cmp, ver) {
130159 return true ;
@@ -334,29 +363,66 @@ mod matches_prerelease_semantic {
334363
335364 #[ test]
336365 fn test_less ( ) {
337- // <I.J.K-0
338- let ref r = req ( "<4.2.1-0" ) ;
339- assert_match_all ( r, & [ "0.0.0" , "4.0.0" ] ) ;
340- assert_match_none ( r, & [ "4.2.1-0" , "4.2.2" , "5.0.0-0" , "5.0.0" ] ) ;
341-
342- // <I.J.K
343- let ref r = req ( "<4.2.1" ) ;
344- assert_match_all ( r, & [ "0.0.0" , "4.0.0" , "4.2.1-0" ] ) ;
345- assert_match_none ( r, & [ "4.2.2" , "5.0.0-0" , "5.0.0" ] ) ;
366+ // <I.J.K equivalent to <I.J.K-0
367+ for r in & [ req ( "<4.2.1" ) , req ( "<4.2.1-0" ) ] {
368+ assert_match_all ( r, & [ "0.0.0" , "4.0.0" ] ) ;
369+ assert_match_none ( r, & [ "4.2.1-0" , "4.2.2" , "5.0.0-0" , "5.0.0" ] ) ;
370+ }
346371
347- // <I.J equivalent to <I.J.0
348- for r in & [ req ( "<4.2" ) , req ( "<4.2.0" ) ] {
349- assert_match_all ( r, & [ "0.0.0" , "4.1.0" , "4.2.0-0" ] ) ;
350- assert_match_none ( r, & [ "4.2.0" , "4.3.0-0" , "4.3.0" ] ) ;
372+ // <I.J equivalent to <I.J.0-0
373+ for r in & [ req ( "<4.2" ) , req ( "<4.2.0-0 " ) ] {
374+ assert_match_all ( r, & [ "0.0.0" , "4.1.0" ] ) ;
375+ assert_match_none ( r, & [ "4.2.0-0" , "4.2.0 ", "4.3.0-0" , "4.3.0" ] ) ;
351376 }
352377
353- // <I equivalent to <I.0.0
354- for r in & [ req ( "<4" ) , req ( "<4.0.0" ) ] {
355- assert_match_all ( r, & [ "0.0.0" , "4.0.0- 0" ] ) ;
356- assert_match_none ( r, & [ "4.0.0" , "5.0.0-1" , "5.0.0" ] ) ;
378+ // <I equivalent to <I.0.0-0
379+ for r in & [ req ( "<4" ) , req ( "<4.0.0-0 " ) ] {
380+ assert_match_all ( r, & [ "0.0.0" , "3.9. 0" ] ) ;
381+ assert_match_none ( r, & [ "4.0.0-0" , "4.0.0 ", "5.0.0-1" , "5.0.0" ] ) ;
357382 }
358383 }
359384
385+ #[ test]
386+ fn test_less_upper_bound ( ) {
387+ // Lower bound without prerelase tag, so upper bound equivalent to <I.J.K-0
388+ for r in & [
389+ req ( ">1.2.3, <2" ) ,
390+ req ( ">1.2.3, <2.0" ) ,
391+ req ( ">1.2.3, <2.0.0" ) ,
392+ req ( ">=1.2.3, <2.0.0" ) ,
393+ req ( ">1.2.3, <2.0.0-0" ) ,
394+ ] {
395+ assert_match_all ( r, & [ "1.2.4" , "1.9.9" ] ) ;
396+ assert_match_none ( r, & [ "2.0.0-0" , "2.0.0" , "2.1.2" ] ) ;
397+ }
398+
399+ // Lower bound has prerelase tag, so upper bound doesn't change.
400+ for r in & [
401+ req ( ">1.2.3-0, <2" ) ,
402+ req ( ">1.2.3-0, <2.0" ) ,
403+ req ( ">1.2.3-0, <2.0.0" ) ,
404+ req ( ">=1.2.3-0, <2.0.0" ) ,
405+ ] {
406+ assert_match_all ( r, & [ "1.2.4" , "1.9.9" , "2.0.0-0" ] ) ;
407+ assert_match_none ( r, & [ "2.0.0" , "2.1.2" ] ) ;
408+ }
409+
410+ for r in & [
411+ req ( ">=2.0.0-0, <2" ) ,
412+ req ( ">=2.0.0-0, <2.0" ) ,
413+ req ( ">=2.0.0-0, <2.0.0" ) ,
414+ ] {
415+ assert_match_all ( r, & [ "2.0.0-0" , "2.0.0-11" ] ) ;
416+ assert_match_none ( r, & [ "0.0.9" , "2.0.0" ] ) ;
417+ }
418+
419+ // There is no intersection between lower bound and upper bound, in this case nothing matches
420+ let ref r = req ( ">5.0.0, <2.0.0" ) ;
421+ assert_match_none ( r, & [ "1.2.3" , "3.0.0" , "6.0.0" ] ) ;
422+ let ref r = req ( ">5.0.0-0, <2.0.0" ) ;
423+ assert_match_none ( r, & [ "1.2.3" , "3.0.0" , "6.0.0" ] ) ;
424+ }
425+
360426 #[ test]
361427 fn test_caret ( ) {
362428 // ^I.J.K.0 (for I>0) — equivalent to >=I.J.K-0, <(I+1).0.0-0
0 commit comments