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