33//! An incompatibility is a set of terms for different packages
44//! that should never be satisfied all together.
55
6- use std:: fmt;
6+ use std:: fmt:: { self , Debug , Display } ;
77use std:: sync:: Arc ;
88
99use crate :: internal:: arena:: { Arena , Id } ;
@@ -32,26 +32,44 @@ use crate::version_set::VersionSet;
3232/// during conflict resolution. More about all this in
3333/// [PubGrub documentation](https://github.com/dart-lang/pub/blob/master/doc/solver.md#incompatibility).
3434#[ derive( Debug , Clone ) ]
35- pub struct Incompatibility < P : Package , VS : VersionSet > {
35+ pub struct Incompatibility < P : Package , VS : VersionSet , M : Eq + Clone + Debug + Display > {
3636 package_terms : SmallMap < P , Term < VS > > ,
37- kind : Kind < P , VS > ,
37+ kind : Kind < P , VS , M > ,
3838}
3939
4040/// Type alias of unique identifiers for incompatibilities.
41- pub type IncompId < P , VS > = Id < Incompatibility < P , VS > > ;
41+ pub type IncompId < P , VS , M > = Id < Incompatibility < P , VS , M > > ;
4242
4343#[ derive( Debug , Clone ) ]
44- enum Kind < P : Package , VS : VersionSet > {
44+ enum Kind < P : Package , VS : VersionSet , M : Eq + Clone + Debug + Display > {
4545 /// Initial incompatibility aiming at picking the root package for the first decision.
46+ ///
47+ /// This incompatibility drives the resolution, it requires that we pick the (virtual) root
48+ /// packages.
4649 NotRoot ( P , VS :: V ) ,
4750 /// There are no versions in the given range for this package.
51+ ///
52+ /// This incompatibility is used when we tried all versions in a range and no version
53+ /// worked, so we have to backtrack
4854 NoVersions ( P , VS ) ,
49- /// Dependencies of the package are unavailable for versions in that range.
50- UnavailableDependencies ( P , VS ) ,
5155 /// Incompatibility coming from the dependencies of a given package.
56+ ///
57+ /// If a@1 depends on b>=1,<2, we create an incompatibility with terms `{a 1, b <1,>=2}` with
58+ /// kind `FromDependencyOf(a, 1, b, >=1,<2)`.
59+ ///
60+ /// We can merge multiple dependents with the same version. For example, if a@1 depends on b and
61+ /// a@2 depends on b, we can say instead a@1||2 depends on b.
5262 FromDependencyOf ( P , VS , P , VS ) ,
5363 /// Derived from two causes. Stores cause ids.
54- DerivedFrom ( IncompId < P , VS > , IncompId < P , VS > ) ,
64+ ///
65+ /// For example, if a -> b and b -> c, we can derive a -> c.
66+ DerivedFrom ( IncompId < P , VS , M > , IncompId < P , VS , M > ) ,
67+ /// The package is unavailable for reasons outside pubgrub.
68+ ///
69+ /// Examples:
70+ /// * The version would require building the package, but builds are disabled.
71+ /// * The package is not available in the cache, but internet access has been disabled.
72+ Custom ( P , VS , M ) ,
5573}
5674
5775/// A Relation describes how a set of terms can be compared to an incompatibility.
@@ -71,7 +89,7 @@ pub enum Relation<P: Package> {
7189 Inconclusive ,
7290}
7391
74- impl < P : Package , VS : VersionSet > Incompatibility < P , VS > {
92+ impl < P : Package , VS : VersionSet , M : Eq + Clone + Debug + Display > Incompatibility < P , VS , M > {
7593 /// Create the initial "not Root" incompatibility.
7694 pub fn not_root ( package : P , version : VS :: V ) -> Self {
7795 Self {
@@ -83,8 +101,7 @@ impl<P: Package, VS: VersionSet> Incompatibility<P, VS> {
83101 }
84102 }
85103
86- /// Create an incompatibility to remember
87- /// that a given set does not contain any version.
104+ /// Create an incompatibility to remember that a given set does not contain any version.
88105 pub fn no_versions ( package : P , term : Term < VS > ) -> Self {
89106 let set = match & term {
90107 Term :: Positive ( r) => r. clone ( ) ,
@@ -96,14 +113,26 @@ impl<P: Package, VS: VersionSet> Incompatibility<P, VS> {
96113 }
97114 }
98115
99- /// Create an incompatibility to remember
100- /// that a package version is not selectable
101- /// because its list of dependencies is unavailable.
102- pub fn unavailable_dependencies ( package : P , version : VS :: V ) -> Self {
116+ /// Create an incompatibility for a reason outside pubgrub.
117+ #[ allow( dead_code) ] // Used by uv
118+ pub fn custom_term ( package : P , term : Term < VS > , metadata : M ) -> Self {
119+ let set = match & term {
120+ Term :: Positive ( r) => r. clone ( ) ,
121+ Term :: Negative ( _) => panic ! ( "No version should have a positive term" ) ,
122+ } ;
123+ Self {
124+ package_terms : SmallMap :: One ( [ ( package. clone ( ) , term) ] ) ,
125+ kind : Kind :: Custom ( package, set, metadata) ,
126+ }
127+ }
128+
129+ /// Create an incompatibility for a reason outside pubgrub.
130+ pub fn custom_version ( package : P , version : VS :: V , metadata : M ) -> Self {
103131 let set = VS :: singleton ( version) ;
132+ let term = Term :: Positive ( set. clone ( ) ) ;
104133 Self {
105- package_terms : SmallMap :: One ( [ ( package. clone ( ) , Term :: Positive ( set . clone ( ) ) ) ] ) ,
106- kind : Kind :: UnavailableDependencies ( package, set) ,
134+ package_terms : SmallMap :: One ( [ ( package. clone ( ) , term ) ] ) ,
135+ kind : Kind :: Custom ( package, set, metadata ) ,
107136 }
108137 }
109138
@@ -135,7 +164,7 @@ impl<P: Package, VS: VersionSet> Incompatibility<P, VS> {
135164 /// When multiple versions of a package depend on the same range of another package,
136165 /// we can merge the two into a single incompatibility.
137166 /// For example, if a@1 depends on b and a@2 depends on b, we can say instead
138- /// a@1 and a@b depend on b.
167+ /// a@1||2 depends on b.
139168 ///
140169 /// It is a special case of prior cause computation where the unified package
141170 /// is the common dependant in the two incompatibilities expressing dependencies.
@@ -231,8 +260,8 @@ impl<P: Package, VS: VersionSet> Incompatibility<P, VS> {
231260 self_id : Id < Self > ,
232261 shared_ids : & Set < Id < Self > > ,
233262 store : & Arena < Self > ,
234- precomputed : & Map < Id < Self > , Arc < DerivationTree < P , VS > > > ,
235- ) -> DerivationTree < P , VS > {
263+ precomputed : & Map < Id < Self > , Arc < DerivationTree < P , VS , M > > > ,
264+ ) -> DerivationTree < P , VS , M > {
236265 match store[ self_id] . kind . clone ( ) {
237266 Kind :: DerivedFrom ( id1, id2) => {
238267 let derived = Derived {
@@ -253,19 +282,28 @@ impl<P: Package, VS: VersionSet> Incompatibility<P, VS> {
253282 DerivationTree :: External ( External :: NotRoot ( package, version) )
254283 }
255284 Kind :: NoVersions ( package, set) => {
256- DerivationTree :: External ( External :: NoVersions ( package, set) )
285+ DerivationTree :: External ( External :: NoVersions ( package. clone ( ) , set. clone ( ) ) )
257286 }
258- Kind :: UnavailableDependencies ( package, set) => {
259- DerivationTree :: External ( External :: UnavailableDependencies ( package, set) )
287+ Kind :: FromDependencyOf ( package, set, dep_package, dep_set) => {
288+ DerivationTree :: External ( External :: FromDependencyOf (
289+ package. clone ( ) ,
290+ set. clone ( ) ,
291+ dep_package. clone ( ) ,
292+ dep_set. clone ( ) ,
293+ ) )
260294 }
261- Kind :: FromDependencyOf ( package, set, dep_package, dep_set) => DerivationTree :: External (
262- External :: FromDependencyOf ( package, set, dep_package, dep_set) ,
263- ) ,
295+ Kind :: Custom ( package, set, metadata) => DerivationTree :: External ( External :: Custom (
296+ package. clone ( ) ,
297+ set. clone ( ) ,
298+ metadata. clone ( ) ,
299+ ) ) ,
264300 }
265301 }
266302}
267303
268- impl < ' a , P : Package , VS : VersionSet + ' a > Incompatibility < P , VS > {
304+ impl < ' a , P : Package , VS : VersionSet + ' a , M : Eq + Clone + Debug + Display + ' a >
305+ Incompatibility < P , VS , M >
306+ {
269307 /// CF definition of Relation enum.
270308 pub fn relation ( & self , terms : impl Fn ( & P ) -> Option < & ' a Term < VS > > ) -> Relation < P > {
271309 let mut relation = Relation :: Satisfied ;
@@ -293,12 +331,17 @@ impl<'a, P: Package, VS: VersionSet + 'a> Incompatibility<P, VS> {
293331 }
294332}
295333
296- impl < P : Package , VS : VersionSet > fmt:: Display for Incompatibility < P , VS > {
334+ impl < P : Package , VS : VersionSet , M : Eq + Clone + Debug + Display > fmt:: Display
335+ for Incompatibility < P , VS , M >
336+ {
297337 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
298338 write ! (
299339 f,
300340 "{}" ,
301- DefaultStringReportFormatter . format_terms( & self . package_terms. as_map( ) )
341+ ReportFormatter :: <P , VS , M >:: format_terms(
342+ & DefaultStringReportFormatter ,
343+ & self . package_terms. as_map( )
344+ )
302345 )
303346 }
304347}
@@ -326,12 +369,12 @@ pub mod tests {
326369 let mut store = Arena :: new( ) ;
327370 let i1 = store. alloc( Incompatibility {
328371 package_terms: SmallMap :: Two ( [ ( "p1" , t1. clone( ) ) , ( "p2" , t2. negate( ) ) ] ) ,
329- kind: Kind :: UnavailableDependencies ( "0 ", Range :: full( ) )
372+ kind: Kind :: <_ , _ , String > :: FromDependencyOf ( "p1" , Range :: full ( ) , "p2 ", Range :: full( ) )
330373 } ) ;
331374
332375 let i2 = store. alloc( Incompatibility {
333376 package_terms: SmallMap :: Two ( [ ( "p2" , t2) , ( "p3" , t3. clone( ) ) ] ) ,
334- kind: Kind :: UnavailableDependencies ( "0 ", Range :: full( ) )
377+ kind: Kind :: <_ , _ , String > :: FromDependencyOf ( "p2" , Range :: full ( ) , "p3 ", Range :: full( ) )
335378 } ) ;
336379
337380 let mut i3 = Map :: default ( ) ;
0 commit comments