1
- use std:: cmp:: Reverse ;
2
-
3
1
use pubgrub:: Range ;
4
2
use rustc_hash:: FxHashMap ;
3
+ use std:: cmp:: Reverse ;
4
+ use std:: collections:: hash_map:: OccupiedEntry ;
5
5
6
6
use crate :: fork_urls:: ForkUrls ;
7
7
use uv_normalize:: PackageName ;
@@ -40,19 +40,22 @@ impl PubGrubPriorities {
40
40
match self . 0 . entry ( name. clone ( ) ) {
41
41
std:: collections:: hash_map:: Entry :: Occupied ( mut entry) => {
42
42
// Preserve the original index.
43
- let index = match entry. get ( ) {
44
- PubGrubPriority :: Unspecified ( Reverse ( index) ) => * index,
45
- PubGrubPriority :: Singleton ( Reverse ( index) ) => * index,
46
- PubGrubPriority :: DirectUrl ( Reverse ( index) ) => * index,
47
- PubGrubPriority :: Root => next,
48
- } ;
43
+ let index = Self :: get_index ( next, & mut entry) ;
49
44
50
45
// Compute the priority.
51
46
let priority = if urls. get ( name) . is_some ( ) {
52
47
PubGrubPriority :: DirectUrl ( Reverse ( index) )
53
48
} else if version. as_singleton ( ) . is_some ( ) {
54
49
PubGrubPriority :: Singleton ( Reverse ( index) )
55
50
} else {
51
+ // Keep the conflict-causing packages to avoid loops where we seesaw between
52
+ // `Unspecified` and `Conflict*`.
53
+ if matches ! (
54
+ entry. get( ) ,
55
+ PubGrubPriority :: ConflictEarly ( _) | PubGrubPriority :: ConflictLate ( _)
56
+ ) {
57
+ return ;
58
+ }
56
59
PubGrubPriority :: Unspecified ( Reverse ( index) )
57
60
} ;
58
61
@@ -77,6 +80,17 @@ impl PubGrubPriorities {
77
80
}
78
81
}
79
82
83
+ fn get_index ( next : usize , entry : & mut OccupiedEntry < PackageName , PubGrubPriority > ) -> usize {
84
+ match entry. get ( ) {
85
+ PubGrubPriority :: ConflictLate ( Reverse ( index) ) => * index,
86
+ PubGrubPriority :: Unspecified ( Reverse ( index) ) => * index,
87
+ PubGrubPriority :: ConflictEarly ( Reverse ( index) ) => * index,
88
+ PubGrubPriority :: Singleton ( Reverse ( index) ) => * index,
89
+ PubGrubPriority :: DirectUrl ( Reverse ( index) ) => * index,
90
+ PubGrubPriority :: Root => next,
91
+ }
92
+ }
93
+
80
94
/// Return the [`PubGrubPriority`] of the given package, if it exists.
81
95
pub ( crate ) fn get ( & self , package : & PubGrubPackage ) -> Option < PubGrubPriority > {
82
96
match & * * package {
@@ -88,6 +102,66 @@ impl PubGrubPriorities {
88
102
PubGrubPackageInner :: Package { name, .. } => self . 0 . get ( name) . copied ( ) ,
89
103
}
90
104
}
105
+
106
+ /// Returns whether the priority was changed, i.e., it's the first time we hit this condition
107
+ /// for the package.
108
+ pub ( crate ) fn make_conflict_early ( & mut self , package : & PubGrubPackage ) -> bool {
109
+ let next = self . 0 . len ( ) ;
110
+ let Some ( name) = package. name_no_root ( ) else {
111
+ // Not a correctness bug
112
+ assert ! (
113
+ !cfg!( debug_assertions) ,
114
+ "URL packages must not be involved in conflict handling"
115
+ ) ;
116
+ return false ;
117
+ } ;
118
+ match self . 0 . entry ( name. clone ( ) ) {
119
+ std:: collections:: hash_map:: Entry :: Occupied ( mut entry) => {
120
+ if matches ! ( entry. get( ) , PubGrubPriority :: ConflictEarly ( _) ) {
121
+ // Already in the right category
122
+ return false ;
123
+ } ;
124
+ let index = Self :: get_index ( next, & mut entry) ;
125
+ entry. insert ( PubGrubPriority :: ConflictEarly ( Reverse ( index) ) ) ;
126
+ true
127
+ }
128
+ std:: collections:: hash_map:: Entry :: Vacant ( entry) => {
129
+ entry. insert ( PubGrubPriority :: ConflictEarly ( Reverse ( next) ) ) ;
130
+ true
131
+ }
132
+ }
133
+ }
134
+
135
+ pub ( crate ) fn make_conflict_late ( & mut self , package : & PubGrubPackage ) -> bool {
136
+ let next = self . 0 . len ( ) ;
137
+ let Some ( name) = package. name_no_root ( ) else {
138
+ // Not a correctness bug
139
+ assert ! (
140
+ !cfg!( debug_assertions) ,
141
+ "URL packages must not be involved in conflict handling"
142
+ ) ;
143
+ return false ;
144
+ } ;
145
+ match self . 0 . entry ( name. clone ( ) ) {
146
+ std:: collections:: hash_map:: Entry :: Occupied ( mut entry) => {
147
+ // The ConflictEarly` match avoids infinite loops.
148
+ if matches ! (
149
+ entry. get( ) ,
150
+ PubGrubPriority :: ConflictLate ( _) | PubGrubPriority :: ConflictEarly ( _)
151
+ ) {
152
+ // Already in the right category
153
+ return false ;
154
+ } ;
155
+ let index = Self :: get_index ( next, & mut entry) ;
156
+ entry. insert ( PubGrubPriority :: ConflictLate ( Reverse ( index) ) ) ;
157
+ true
158
+ }
159
+ std:: collections:: hash_map:: Entry :: Vacant ( entry) => {
160
+ entry. insert ( PubGrubPriority :: ConflictLate ( Reverse ( next) ) ) ;
161
+ true
162
+ }
163
+ }
164
+ }
91
165
}
92
166
93
167
#[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord ) ]
@@ -101,6 +175,15 @@ pub(crate) enum PubGrubPriority {
101
175
/// in the dependency graph.
102
176
Unspecified ( Reverse < usize > ) ,
103
177
178
+ /// Selected version of this package were often the culprit of rejecting another package, so
179
+ /// it's deprioritized behind `ConflictEarly`. It's still the higher than `Unspecified` to
180
+ /// conflict before selecting unrelated packages.
181
+ ConflictLate ( Reverse < usize > ) ,
182
+
183
+ /// Selected version of this package were often rejected, so it's prioritized over
184
+ /// `ConflictLate`.
185
+ ConflictEarly ( Reverse < usize > ) ,
186
+
104
187
/// The version range is constrained to a single version (e.g., with the `==` operator).
105
188
Singleton ( Reverse < usize > ) ,
106
189
0 commit comments