@@ -235,6 +235,93 @@ 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
309
+ . segments
310
+ . iter ( )
311
+ . find ( |& seg| match seg {
312
+ ( _, Some ( s2) ) => o1 < s2,
313
+ _ => true ,
314
+ } )
315
+ . is_some ( )
316
+ {
317
+ return Relation :: Inconclusive ;
318
+ }
319
+ }
320
+ } ;
321
+ Relation :: Contradicted
322
+ }
323
+ }
324
+
238
325
// Other useful functions.
239
326
impl < V : Version > Range < V > {
240
327
/// Check if a range contains a given version.
@@ -379,6 +466,53 @@ pub mod tests {
379
466
assert_eq!( r1. intersection( & r2) . contains( & version) , r1. contains( & version) && r2. contains( & version) ) ;
380
467
}
381
468
469
+ // Testing relation -----------------------------------
470
+
471
+ #[ test]
472
+ fn relation_with_any_is_satisfied( range in strategy( ) ) {
473
+ prop_assert_eq!( range. relation( & Range :: any( ) ) , Relation :: Satisfied ) ;
474
+ }
475
+
476
+ #[ test]
477
+ fn relation_with_none_is_contradicted( range in strategy( ) ) {
478
+ prop_assert_eq!( range. relation( & Range :: none( ) ) , if range == Range :: none( ) {
479
+ Relation :: Satisfied
480
+ } else {
481
+ Relation :: Contradicted
482
+ } ) ;
483
+ }
484
+
485
+ #[ test]
486
+ fn relation_with_self_is_satisfied( range in strategy( ) ) {
487
+ prop_assert_eq!( range. relation( & range) , Relation :: Satisfied ) ;
488
+ }
489
+
490
+ #[ test]
491
+ fn relation_matchs_intersection( r1 in strategy( ) , r2 in strategy( ) ) {
492
+ let full_intersection = r1. intersection( & r2) ;
493
+ let by_intersection = if full_intersection == r2 {
494
+ Relation :: Satisfied
495
+ } else if full_intersection == Range :: none( ) {
496
+ Relation :: Contradicted
497
+ } else {
498
+ Relation :: Inconclusive
499
+ } ;
500
+ prop_assert_eq!( by_intersection, r2. relation( & r1) ) ;
501
+ }
502
+
503
+ #[ test]
504
+ fn relation_contains_both( r1 in strategy( ) , r2 in strategy( ) , version in version_strat( ) ) {
505
+ match r1. relation( & r2) {
506
+ Relation :: Satisfied => {
507
+ prop_assert!( r2. contains( & version) || !r1. contains( & version) ) ;
508
+ }
509
+ Relation :: Contradicted => {
510
+ prop_assert!( !( r1. contains( & version) && r2. contains( & version) ) ) ;
511
+ }
512
+ _ => { }
513
+ }
514
+ }
515
+
382
516
// Testing union -----------------------------------
383
517
384
518
#[ test]
0 commit comments