@@ -237,6 +237,88 @@ impl<V: Version> Range<V> {
237237 }
238238}
239239
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+
240322// Other useful functions.
241323impl < V : Version > Range < V > {
242324 /// Check if a range contains a given version.
@@ -381,6 +463,53 @@ pub mod tests {
381463 assert_eq!( r1. intersection( & r2) . contains( & version) , r1. contains( & version) && r2. contains( & version) ) ;
382464 }
383465
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+
384513 // Testing union -----------------------------------
385514
386515 #[ test]
0 commit comments