11use std:: collections:: hash_map:: Entry ;
2- use std:: collections:: BTreeMap ;
32
43use rustc_hash:: FxHashMap ;
54
65use distribution_types:: { CachedRegistryDist , Hashed , IndexLocations , IndexUrl } ;
7- use pep440_rs:: Version ;
86use platform_tags:: Tags ;
97use uv_cache:: { Cache , CacheBucket , WheelCache } ;
108use uv_fs:: { directories, files, symlinks} ;
@@ -14,14 +12,23 @@ use uv_types::HashStrategy;
1412use crate :: index:: cached_wheel:: CachedWheel ;
1513use crate :: source:: { HttpRevisionPointer , LocalRevisionPointer , HTTP_REVISION , LOCAL_REVISION } ;
1614
15+ /// An entry in the [`RegistryWheelIndex`].
16+ #[ derive( Debug , Clone , Hash , PartialEq , Eq ) ]
17+ pub struct IndexEntry {
18+ /// The cached distribution.
19+ pub dist : CachedRegistryDist ,
20+ /// Whether the wheel was built from source (true), or downloaded from the registry directly (false).
21+ pub built : bool ,
22+ }
23+
1724/// A local index of distributions that originate from a registry, like `PyPI`.
1825#[ derive( Debug ) ]
1926pub struct RegistryWheelIndex < ' a > {
2027 cache : & ' a Cache ,
2128 tags : & ' a Tags ,
2229 index_locations : & ' a IndexLocations ,
2330 hasher : & ' a HashStrategy ,
24- index : FxHashMap < & ' a PackageName , BTreeMap < Version , CachedRegistryDist > > ,
31+ index : FxHashMap < & ' a PackageName , Vec < IndexEntry > > ,
2532}
2633
2734impl < ' a > RegistryWheelIndex < ' a > {
@@ -44,26 +51,12 @@ impl<'a> RegistryWheelIndex<'a> {
4451 /// Return an iterator over available wheels for a given package.
4552 ///
4653 /// If the package is not yet indexed, this will index the package by reading from the cache.
47- pub fn get (
48- & mut self ,
49- name : & ' a PackageName ,
50- ) -> impl Iterator < Item = ( & Version , & CachedRegistryDist ) > {
54+ pub fn get ( & mut self , name : & ' a PackageName ) -> impl Iterator < Item = & IndexEntry > {
5155 self . get_impl ( name) . iter ( ) . rev ( )
5256 }
5357
54- /// Get the best wheel for the given package name and version.
55- ///
56- /// If the package is not yet indexed, this will index the package by reading from the cache.
57- pub fn get_version (
58- & mut self ,
59- name : & ' a PackageName ,
60- version : & Version ,
61- ) -> Option < & CachedRegistryDist > {
62- self . get_impl ( name) . get ( version)
63- }
64-
6558 /// Get an entry in the index.
66- fn get_impl ( & mut self , name : & ' a PackageName ) -> & BTreeMap < Version , CachedRegistryDist > {
59+ fn get_impl ( & mut self , name : & ' a PackageName ) -> & [ IndexEntry ] {
6760 let versions = match self . index . entry ( name) {
6861 Entry :: Occupied ( entry) => entry. into_mut ( ) ,
6962 Entry :: Vacant ( entry) => entry. insert ( Self :: index (
@@ -84,8 +77,8 @@ impl<'a> RegistryWheelIndex<'a> {
8477 tags : & Tags ,
8578 index_locations : & IndexLocations ,
8679 hasher : & HashStrategy ,
87- ) -> BTreeMap < Version , CachedRegistryDist > {
88- let mut versions = BTreeMap :: new ( ) ;
80+ ) -> Vec < IndexEntry > {
81+ let mut entries = vec ! [ ] ;
8982
9083 // Collect into owned `IndexUrl`.
9184 let flat_index_urls: Vec < IndexUrl > = index_locations
@@ -113,12 +106,19 @@ impl<'a> RegistryWheelIndex<'a> {
113106 if let Some ( wheel) =
114107 CachedWheel :: from_http_pointer ( wheel_dir. join ( file) , cache)
115108 {
116- // Enforce hash-checking based on the built distribution.
117- if wheel. satisfies (
118- hasher
119- . get_package ( & wheel. filename . name , & wheel. filename . version ) ,
120- ) {
121- Self :: add_wheel ( wheel, tags, & mut versions) ;
109+ if wheel. filename . compatibility ( tags) . is_compatible ( ) {
110+ // Enforce hash-checking based on the built distribution.
111+ if wheel. satisfies (
112+ hasher. get_package (
113+ & wheel. filename . name ,
114+ & wheel. filename . version ,
115+ ) ,
116+ ) {
117+ entries. push ( IndexEntry {
118+ dist : wheel. into_registry_dist ( ) ,
119+ built : false ,
120+ } ) ;
121+ }
122122 }
123123 }
124124 }
@@ -132,12 +132,19 @@ impl<'a> RegistryWheelIndex<'a> {
132132 if let Some ( wheel) =
133133 CachedWheel :: from_local_pointer ( wheel_dir. join ( file) , cache)
134134 {
135- // Enforce hash-checking based on the built distribution.
136- if wheel. satisfies (
137- hasher
138- . get_package ( & wheel. filename . name , & wheel. filename . version ) ,
139- ) {
140- Self :: add_wheel ( wheel, tags, & mut versions) ;
135+ if wheel. filename . compatibility ( tags) . is_compatible ( ) {
136+ // Enforce hash-checking based on the built distribution.
137+ if wheel. satisfies (
138+ hasher. get_package (
139+ & wheel. filename . name ,
140+ & wheel. filename . version ,
141+ ) ,
142+ ) {
143+ entries. push ( IndexEntry {
144+ dist : wheel. into_registry_dist ( ) ,
145+ built : false ,
146+ } ) ;
147+ }
141148 }
142149 }
143150 }
@@ -182,38 +189,41 @@ impl<'a> RegistryWheelIndex<'a> {
182189 if let Some ( revision) = revision {
183190 for wheel_dir in symlinks ( cache_shard. join ( revision. id ( ) ) ) {
184191 if let Some ( wheel) = CachedWheel :: from_built_source ( wheel_dir) {
185- // Enforce hash-checking based on the source distribution.
186- if revision. satisfies (
187- hasher. get_package ( & wheel. filename . name , & wheel. filename . version ) ,
188- ) {
189- Self :: add_wheel ( wheel, tags, & mut versions) ;
192+ if wheel. filename . compatibility ( tags) . is_compatible ( ) {
193+ // Enforce hash-checking based on the source distribution.
194+ if revision. satisfies (
195+ hasher
196+ . get_package ( & wheel. filename . name , & wheel. filename . version ) ,
197+ ) {
198+ entries. push ( IndexEntry {
199+ dist : wheel. into_registry_dist ( ) ,
200+ built : true ,
201+ } ) ;
202+ }
190203 }
191204 }
192205 }
193206 }
194207 }
195208 }
196209
197- versions
198- }
199-
200- /// Add the [`CachedWheel`] to the index.
201- fn add_wheel (
202- wheel : CachedWheel ,
203- tags : & Tags ,
204- versions : & mut BTreeMap < Version , CachedRegistryDist > ,
205- ) {
206- let dist_info = wheel. into_registry_dist ( ) ;
207-
208- // Pick the wheel with the highest priority
209- let compatibility = dist_info. filename . compatibility ( tags) ;
210- if let Some ( existing) = versions. get_mut ( & dist_info. filename . version ) {
211- // Override if we have better compatibility
212- if compatibility > existing. filename . compatibility ( tags) {
213- * existing = dist_info;
214- }
215- } else if compatibility. is_compatible ( ) {
216- versions. insert ( dist_info. filename . version . clone ( ) , dist_info) ;
217- }
210+ // Sort the cached distributions by (1) version, (2) compatibility, and (3) build status.
211+ // We want the highest versions, with the greatest compatibility, that were built from source.
212+ // at the end of the list.
213+ entries. sort_unstable_by ( |a, b| {
214+ a. dist
215+ . filename
216+ . version
217+ . cmp ( & b. dist . filename . version )
218+ . then_with ( || {
219+ a. dist
220+ . filename
221+ . compatibility ( tags)
222+ . cmp ( & b. dist . filename . compatibility ( tags) )
223+ . then_with ( || a. built . cmp ( & b. built ) )
224+ } )
225+ } ) ;
226+
227+ entries
218228 }
219229}
0 commit comments