@@ -235,6 +235,88 @@ impl<V: Version> Range<V> {
235
235
}
236
236
}
237
237
238
+ /// Describe a relation between a set of terms S and another term t.
239
+ ///
240
+ /// As a shorthand, we say that a term v
241
+ /// satisfies or contradicts a term t if {v} satisfies or contradicts it.
242
+ #[ derive( Eq , PartialEq , Debug ) ]
243
+ pub ( crate ) enum Relation {
244
+ /// We say that a set of terms S "satisfies" a term t
245
+ /// if t must be true whenever every term in S is true.
246
+ Satisfied ,
247
+ /// Conversely, S "contradicts" t if t must be false
248
+ /// whenever every term in S is true.
249
+ Contradicted ,
250
+ /// If neither of these is true we say that S is "inconclusive" for t.
251
+ Inconclusive ,
252
+ }
253
+
254
+ // Set operations.
255
+ impl < V : Version > Range < V > {
256
+ /// Compute the relation of two sets of versions.
257
+ pub ( crate ) fn relation ( & self , other : & Self ) -> Relation {
258
+ let mut state = None ;
259
+ for s in & self . segments {
260
+ match other. contains_interval ( s) {
261
+ Relation :: Satisfied => match state {
262
+ None | Some ( Relation :: Satisfied ) => state = Some ( Relation :: Satisfied ) ,
263
+ _ => return Relation :: Inconclusive ,
264
+ } ,
265
+ Relation :: Inconclusive => return Relation :: Inconclusive ,
266
+ Relation :: Contradicted => match state {
267
+ None | Some ( Relation :: Contradicted ) => state = Some ( Relation :: Contradicted ) ,
268
+ _ => return Relation :: Inconclusive ,
269
+ } ,
270
+ } ;
271
+ }
272
+ state. unwrap_or ( Relation :: Satisfied )
273
+ }
274
+
275
+ fn contains_interval ( & self , other : & Interval < V > ) -> Relation {
276
+ match other {
277
+ ( o1, Some ( o2) ) => {
278
+ for seg in & self . segments {
279
+ match seg {
280
+ ( s1, Some ( s2) ) => {
281
+ if o2 < s1 {
282
+ break ;
283
+ }
284
+ if s1 <= o1 && o2 <= s2 {
285
+ return Relation :: Satisfied ;
286
+ }
287
+ if !( s1 >= o2 || o1 >= s2) {
288
+ return Relation :: Inconclusive ;
289
+ }
290
+ }
291
+ ( s1, None ) => {
292
+ if s1 <= o1 {
293
+ return Relation :: Satisfied ;
294
+ }
295
+ if s1 < o2 {
296
+ return Relation :: Inconclusive ;
297
+ }
298
+ }
299
+ }
300
+ }
301
+ }
302
+ ( o1, None ) => {
303
+ if let Some ( ( s1, None ) ) = self . segments . iter ( ) . rev ( ) . next ( ) {
304
+ if s1 <= o1 {
305
+ return Relation :: Satisfied ;
306
+ }
307
+ }
308
+ if self . segments . iter ( ) . any ( |seg| match seg {
309
+ ( _, Some ( s2) ) => o1 < s2,
310
+ _ => true ,
311
+ } ) {
312
+ return Relation :: Inconclusive ;
313
+ }
314
+ }
315
+ } ;
316
+ Relation :: Contradicted
317
+ }
318
+ }
319
+
238
320
// Other useful functions.
239
321
impl < V : Version > Range < V > {
240
322
/// Check if a range contains a given version.
@@ -379,6 +461,53 @@ pub mod tests {
379
461
assert_eq!( r1. intersection( & r2) . contains( & version) , r1. contains( & version) && r2. contains( & version) ) ;
380
462
}
381
463
464
+ // Testing relation -----------------------------------
465
+
466
+ #[ test]
467
+ fn relation_with_any_is_satisfied( range in strategy( ) ) {
468
+ prop_assert_eq!( range. relation( & Range :: any( ) ) , Relation :: Satisfied ) ;
469
+ }
470
+
471
+ #[ test]
472
+ fn relation_with_none_is_contradicted( range in strategy( ) ) {
473
+ prop_assert_eq!( range. relation( & Range :: none( ) ) , if range == Range :: none( ) {
474
+ Relation :: Satisfied
475
+ } else {
476
+ Relation :: Contradicted
477
+ } ) ;
478
+ }
479
+
480
+ #[ test]
481
+ fn relation_with_self_is_satisfied( range in strategy( ) ) {
482
+ prop_assert_eq!( range. relation( & range) , Relation :: Satisfied ) ;
483
+ }
484
+
485
+ #[ test]
486
+ fn relation_matchs_intersection( r1 in strategy( ) , r2 in strategy( ) ) {
487
+ let full_intersection = r1. intersection( & r2) ;
488
+ let by_intersection = if full_intersection == r2 {
489
+ Relation :: Satisfied
490
+ } else if full_intersection == Range :: none( ) {
491
+ Relation :: Contradicted
492
+ } else {
493
+ Relation :: Inconclusive
494
+ } ;
495
+ prop_assert_eq!( by_intersection, r2. relation( & r1) ) ;
496
+ }
497
+
498
+ #[ test]
499
+ fn relation_contains_both( r1 in strategy( ) , r2 in strategy( ) , version in version_strat( ) ) {
500
+ match r1. relation( & r2) {
501
+ Relation :: Satisfied => {
502
+ prop_assert!( r2. contains( & version) || !r1. contains( & version) ) ;
503
+ }
504
+ Relation :: Contradicted => {
505
+ prop_assert!( !( r1. contains( & version) && r2. contains( & version) ) ) ;
506
+ }
507
+ _ => { }
508
+ }
509
+ }
510
+
382
511
// Testing union -----------------------------------
383
512
384
513
#[ test]
0 commit comments