7
7
use semver:: { Comparator , Op , Prerelease , Version , VersionReq } ;
8
8
9
9
pub ( 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
+ } ) ;
10
18
for cmp in & req. comparators {
11
- if !matches_prerelease_impl ( cmp, ver) {
19
+ if !matches_prerelease_impl ( cmp, ver, lower_bound_prerelease ) {
12
20
return false ;
13
21
}
14
22
}
15
23
16
24
true
17
25
}
18
26
19
- fn matches_prerelease_impl ( cmp : & Comparator , ver : & Version ) -> bool {
27
+ fn matches_prerelease_impl ( cmp : & Comparator , ver : & Version , lower_bound_prerelease : & bool ) -> bool {
20
28
match cmp. op {
21
29
Op :: Exact | Op :: Wildcard => matches_exact_prerelease ( cmp, ver) ,
22
30
Op :: Greater => matches_greater ( cmp, ver) ,
@@ -26,7 +34,13 @@ fn matches_prerelease_impl(cmp: &Comparator, ver: &Version) -> bool {
26
34
}
27
35
matches_greater ( cmp, ver)
28
36
}
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
+ }
30
44
Op :: LessEq => {
31
45
if matches_exact_prerelease ( cmp, ver) {
32
46
return true ;
@@ -125,6 +139,21 @@ fn fill_partial_req(cmp: &Comparator) -> Comparator {
125
139
cmp
126
140
}
127
141
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
+
128
157
fn matches_exact_prerelease ( cmp : & Comparator , ver : & Version ) -> bool {
129
158
if matches_exact ( cmp, ver) {
130
159
return true ;
@@ -334,29 +363,66 @@ mod matches_prerelease_semantic {
334
363
335
364
#[ test]
336
365
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
+ }
346
371
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" ] ) ;
351
376
}
352
377
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" ] ) ;
357
382
}
358
383
}
359
384
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
+
360
426
#[ test]
361
427
fn test_caret ( ) {
362
428
// ^I.J.K.0 (for I>0) — equivalent to >=I.J.K-0, <(I+1).0.0-0
0 commit comments