Skip to main content

uv_resolver/
version_map.rs

1use std::collections::Bound;
2use std::collections::btree_map::{BTreeMap, Entry};
3use std::ops::RangeBounds;
4use std::sync::OnceLock;
5
6use pubgrub::Ranges;
7use rustc_hash::FxHashMap;
8use tracing::{instrument, trace};
9
10use uv_client::{FlatIndexEntry, OwnedArchive, SimpleDetailMetadata, VersionFiles};
11use uv_configuration::BuildOptions;
12use uv_distribution_filename::{DistFilename, WheelFilename};
13use uv_distribution_types::{
14    HashComparison, IncompatibleSource, IncompatibleWheel, IndexUrl, PrioritizedDist,
15    RegistryBuiltWheel, RegistrySourceDist, RequiresPython, SourceDistCompatibility,
16    WheelCompatibility,
17};
18use uv_normalize::PackageName;
19use uv_pep440::Version;
20use uv_platform_tags::{IncompatibleTag, TagCompatibility, Tags};
21use uv_pypi_types::{HashDigest, ResolutionMetadata, Yanked};
22use uv_types::HashStrategy;
23use uv_warnings::warn_user_once;
24
25use crate::flat_index::FlatDistributions;
26use crate::{ExcludeNewerValue, yanks::AllowedYanks};
27
28/// A map from versions to distributions.
29#[derive(Debug)]
30pub struct VersionMap {
31    /// The inner representation of the version map.
32    inner: VersionMapInner,
33}
34
35impl VersionMap {
36    /// Initialize a [`VersionMap`] from the given metadata.
37    ///
38    /// Note it is possible for files to have a different yank status per PEP 592 but in the official
39    /// PyPI warehouse this cannot happen.
40    ///
41    /// Here, we track if each file is yanked separately. If a release is partially yanked, the
42    /// unyanked distributions _can_ be used.
43    ///
44    /// PEP 592: <https://peps.python.org/pep-0592/#warehouse-pypi-implementation-notes>
45    #[instrument(skip_all, fields(package_name))]
46    pub(crate) fn from_simple_metadata(
47        simple_metadata: OwnedArchive<SimpleDetailMetadata>,
48        package_name: &PackageName,
49        index: &IndexUrl,
50        tags: Option<&Tags>,
51        requires_python: &RequiresPython,
52        allowed_yanks: &AllowedYanks,
53        hasher: &HashStrategy,
54        exclude_newer: Option<ExcludeNewerValue>,
55        flat_index: Option<FlatDistributions>,
56        build_options: &BuildOptions,
57    ) -> Self {
58        let mut stable = false;
59        let mut local = false;
60        let mut map = BTreeMap::new();
61        let mut core_metadata = FxHashMap::default();
62        // Create stubs for each entry in simple metadata. The full conversion
63        // from a `VersionFiles` to a PrioritizedDist for each version
64        // isn't done until that specific version is requested.
65        for (datum_index, datum) in simple_metadata.iter().enumerate() {
66            // Deserialize the version.
67            let version = rkyv::deserialize::<Version, rkyv::rancor::Error>(&datum.version)
68                .expect("archived version always deserializes");
69
70            // Deserialize the metadata.
71            let core_metadatum =
72                rkyv::deserialize::<Option<ResolutionMetadata>, rkyv::rancor::Error>(
73                    &datum.metadata,
74                )
75                .expect("archived metadata always deserializes");
76            if let Some(core_metadatum) = core_metadatum {
77                core_metadata.insert(version.clone(), core_metadatum);
78            }
79
80            stable |= version.is_stable();
81            local |= version.is_local();
82            map.insert(
83                version,
84                LazyPrioritizedDist::OnlySimple(SimplePrioritizedDist {
85                    datum_index,
86                    dist: OnceLock::new(),
87                }),
88            );
89        }
90        // If a set of flat distributions have been given, we need to add those
91        // to our map of entries as well.
92        for (version, prioritized_dist) in flat_index.into_iter().flatten() {
93            stable |= version.is_stable();
94            match map.entry(version) {
95                Entry::Vacant(e) => {
96                    e.insert(LazyPrioritizedDist::OnlyFlat(prioritized_dist));
97                }
98                // When there is both a `VersionFiles` (from the "simple"
99                // metadata) and a flat distribution for the same version of
100                // a package, we store both and "merge" them into a single
101                // `PrioritizedDist` upon access later.
102                Entry::Occupied(e) => match e.remove_entry() {
103                    (version, LazyPrioritizedDist::OnlySimple(simple_dist)) => {
104                        map.insert(
105                            version,
106                            LazyPrioritizedDist::Both {
107                                flat: prioritized_dist,
108                                simple: simple_dist,
109                            },
110                        );
111                    }
112                    _ => unreachable!(),
113                },
114            }
115        }
116        Self {
117            inner: VersionMapInner::Lazy(VersionMapLazy {
118                map,
119                stable,
120                local,
121                core_metadata,
122                simple_metadata,
123                no_binary: build_options.no_binary_package(package_name),
124                no_build: build_options.no_build_package(package_name),
125                index: index.clone(),
126                tags: tags.cloned(),
127                allowed_yanks: allowed_yanks.clone(),
128                hasher: hasher.clone(),
129                requires_python: requires_python.clone(),
130                exclude_newer,
131            }),
132        }
133    }
134
135    #[instrument(skip_all, fields(package_name))]
136    pub(crate) fn from_flat_metadata(
137        flat_metadata: Vec<FlatIndexEntry>,
138        tags: Option<&Tags>,
139        hasher: &HashStrategy,
140        build_options: &BuildOptions,
141    ) -> Self {
142        let mut stable = false;
143        let mut local = false;
144        let mut map = BTreeMap::new();
145
146        for (version, prioritized_dist) in
147            FlatDistributions::from_entries(flat_metadata, tags, hasher, build_options)
148        {
149            stable |= version.is_stable();
150            local |= version.is_local();
151            map.insert(version, prioritized_dist);
152        }
153
154        Self {
155            inner: VersionMapInner::Eager(VersionMapEager { map, stable, local }),
156        }
157    }
158
159    /// Return the [`ResolutionMetadata`] for the given version, if any.
160    pub fn get_metadata(&self, version: &Version) -> Option<&ResolutionMetadata> {
161        match self.inner {
162            VersionMapInner::Eager(_) => None,
163            VersionMapInner::Lazy(ref lazy) => lazy.core_metadata.get(version),
164        }
165    }
166
167    /// Return the [`DistFile`] for the given version, if any.
168    pub(crate) fn get(&self, version: &Version) -> Option<&PrioritizedDist> {
169        match self.inner {
170            VersionMapInner::Eager(ref eager) => eager.map.get(version),
171            VersionMapInner::Lazy(ref lazy) => lazy.get(version),
172        }
173    }
174
175    /// Return an iterator over the versions in this map.
176    pub(crate) fn versions(&self) -> impl DoubleEndedIterator<Item = &Version> {
177        match &self.inner {
178            VersionMapInner::Eager(eager) => either::Either::Left(eager.map.keys()),
179            VersionMapInner::Lazy(lazy) => either::Either::Right(lazy.map.keys()),
180        }
181    }
182
183    /// Return the index URL where this package came from.
184    pub(crate) fn index(&self) -> Option<&IndexUrl> {
185        match &self.inner {
186            VersionMapInner::Eager(_) => None,
187            VersionMapInner::Lazy(lazy) => Some(&lazy.index),
188        }
189    }
190
191    /// Return the effective `exclude-newer` cutoff for this version map, if any.
192    pub(crate) fn exclude_newer(&self) -> Option<&ExcludeNewerValue> {
193        match &self.inner {
194            VersionMapInner::Eager(_) => None,
195            VersionMapInner::Lazy(lazy) => lazy.exclude_newer.as_ref(),
196        }
197    }
198
199    /// Return an iterator over the versions and distributions.
200    ///
201    /// Note that the value returned in this iterator is a [`VersionMapDist`],
202    /// which can be used to lazily request a [`CompatibleDist`]. This is
203    /// useful in cases where one can skip materializing a full distribution
204    /// for each version.
205    pub(crate) fn iter(
206        &self,
207        range: &Ranges<Version>,
208    ) -> impl DoubleEndedIterator<Item = (&Version, VersionMapDistHandle<'_>)> {
209        // Performance optimization: If we only have a single version, return that version directly.
210        if let Some(version) = range.as_singleton() {
211            either::Either::Left(match self.inner {
212                VersionMapInner::Eager(ref eager) => {
213                    either::Either::Left(eager.map.get_key_value(version).into_iter().map(
214                        move |(version, dist)| {
215                            let version_map_dist = VersionMapDistHandle {
216                                inner: VersionMapDistHandleInner::Eager(dist),
217                            };
218                            (version, version_map_dist)
219                        },
220                    ))
221                }
222                VersionMapInner::Lazy(ref lazy) => {
223                    either::Either::Right(lazy.map.get_key_value(version).into_iter().map(
224                        move |(version, dist)| {
225                            let version_map_dist = VersionMapDistHandle {
226                                inner: VersionMapDistHandleInner::Lazy { lazy, dist },
227                            };
228                            (version, version_map_dist)
229                        },
230                    ))
231                }
232            })
233        } else {
234            either::Either::Right(match self.inner {
235                VersionMapInner::Eager(ref eager) => {
236                    either::Either::Left(eager.map.range(BoundingRange::from(range)).map(
237                        |(version, dist)| {
238                            let version_map_dist = VersionMapDistHandle {
239                                inner: VersionMapDistHandleInner::Eager(dist),
240                            };
241                            (version, version_map_dist)
242                        },
243                    ))
244                }
245                VersionMapInner::Lazy(ref lazy) => {
246                    either::Either::Right(lazy.map.range(BoundingRange::from(range)).map(
247                        |(version, dist)| {
248                            let version_map_dist = VersionMapDistHandle {
249                                inner: VersionMapDistHandleInner::Lazy { lazy, dist },
250                            };
251                            (version, version_map_dist)
252                        },
253                    ))
254                }
255            })
256        }
257    }
258
259    /// Return the [`Hashes`] for the given version, if any.
260    pub(crate) fn hashes(&self, version: &Version) -> Option<&[HashDigest]> {
261        match self.inner {
262            VersionMapInner::Eager(ref eager) => {
263                eager.map.get(version).map(PrioritizedDist::hashes)
264            }
265            VersionMapInner::Lazy(ref lazy) => lazy.get(version).map(PrioritizedDist::hashes),
266        }
267    }
268
269    /// Returns the total number of distinct versions in this map.
270    ///
271    /// Note that this may include versions of distributions that are not
272    /// usable in the current environment.
273    pub(crate) fn len(&self) -> usize {
274        match self.inner {
275            VersionMapInner::Eager(VersionMapEager { ref map, .. }) => map.len(),
276            VersionMapInner::Lazy(VersionMapLazy { ref map, .. }) => map.len(),
277        }
278    }
279
280    /// Returns `true` if the map contains at least one stable (non-pre-release) version.
281    pub(crate) fn stable(&self) -> bool {
282        match self.inner {
283            VersionMapInner::Eager(ref map) => map.stable,
284            VersionMapInner::Lazy(ref map) => map.stable,
285        }
286    }
287
288    /// Returns `true` if the map contains at least one local version (e.g., `2.6.0+cpu`).
289    pub(crate) fn local(&self) -> bool {
290        match self.inner {
291            VersionMapInner::Eager(ref map) => map.local,
292            VersionMapInner::Lazy(ref map) => map.local,
293        }
294    }
295}
296
297impl From<FlatDistributions> for VersionMap {
298    fn from(flat_index: FlatDistributions) -> Self {
299        let stable = flat_index.iter().any(|(version, _)| version.is_stable());
300        let local = flat_index.iter().any(|(version, _)| version.is_local());
301        let map = flat_index.into();
302        Self {
303            inner: VersionMapInner::Eager(VersionMapEager { map, stable, local }),
304        }
305    }
306}
307
308/// A lazily initialized distribution.
309///
310/// This permits access to a handle that can be turned into a resolvable
311/// distribution when desired. This is coupled with a `Version` in
312/// [`VersionMap::iter`] to permit iteration over all items in a map without
313/// necessarily constructing a distribution for every version if it isn't
314/// needed.
315///
316/// Note that because of laziness, not all such items can be turned into
317/// a valid distribution. For example, if in the process of building a
318/// distribution no compatible wheel or source distribution could be found,
319/// then building a `CompatibleDist` will fail.
320pub(crate) struct VersionMapDistHandle<'a> {
321    inner: VersionMapDistHandleInner<'a>,
322}
323
324enum VersionMapDistHandleInner<'a> {
325    Eager(&'a PrioritizedDist),
326    Lazy {
327        lazy: &'a VersionMapLazy,
328        dist: &'a LazyPrioritizedDist,
329    },
330}
331
332impl<'a> VersionMapDistHandle<'a> {
333    /// Returns a prioritized distribution from this handle.
334    pub(crate) fn prioritized_dist(&self) -> Option<&'a PrioritizedDist> {
335        match self.inner {
336            VersionMapDistHandleInner::Eager(dist) => Some(dist),
337            VersionMapDistHandleInner::Lazy { lazy, dist } => Some(lazy.get_lazy(dist)?),
338        }
339    }
340}
341
342/// The kind of internal version map we have.
343#[derive(Debug)]
344#[expect(clippy::large_enum_variant)]
345enum VersionMapInner {
346    /// All distributions are fully materialized in memory.
347    ///
348    /// This usually happens when one needs a `VersionMap` from a
349    /// `FlatDistributions`.
350    Eager(VersionMapEager),
351    /// Some distributions might be fully materialized (i.e., by initializing
352    /// a `VersionMap` with a `FlatDistributions`), but some distributions
353    /// might still be in their "raw" `SimpleDetailMetadata` format. In this case, a
354    /// `PrioritizedDist` isn't actually created in memory until the
355    /// specific version has been requested.
356    Lazy(VersionMapLazy),
357}
358
359/// A map from versions to distributions that are fully materialized in memory.
360#[derive(Debug)]
361struct VersionMapEager {
362    /// A map from version to distribution.
363    map: BTreeMap<Version, PrioritizedDist>,
364    /// Whether the version map contains at least one stable (non-pre-release) version.
365    stable: bool,
366    /// Whether the version map contains at least one local version.
367    local: bool,
368}
369
370/// A map that lazily materializes some prioritized distributions upon access.
371///
372/// The idea here is that some packages have a lot of versions published, and
373/// needing to materialize a full `VersionMap` with all corresponding metadata
374/// for every version in memory is expensive. Since a `SimpleDetailMetadata` can be
375/// materialized with very little cost (via `rkyv` in the warm cached case),
376/// avoiding another conversion step into a fully filled out `VersionMap` can
377/// provide substantial savings in some cases.
378#[derive(Debug)]
379struct VersionMapLazy {
380    /// A map from version to possibly-initialized distribution.
381    map: BTreeMap<Version, LazyPrioritizedDist>,
382    /// Whether the version map contains at least one stable (non-pre-release) version.
383    stable: bool,
384    /// Whether the version map contains at least one local version.
385    local: bool,
386    /// The pre-populated metadata for each version.
387    core_metadata: FxHashMap<Version, ResolutionMetadata>,
388    /// The raw simple metadata from which `PrioritizedDist`s should
389    /// be constructed.
390    simple_metadata: OwnedArchive<SimpleDetailMetadata>,
391    /// When true, wheels aren't allowed.
392    no_binary: bool,
393    /// When true, source dists aren't allowed.
394    no_build: bool,
395    /// The URL of the index where this package came from.
396    index: IndexUrl,
397    /// The set of compatibility tags that determines whether a wheel is usable
398    /// in the current environment.
399    tags: Option<Tags>,
400    /// Whether files newer than this timestamp should be excluded or not.
401    exclude_newer: Option<ExcludeNewerValue>,
402    /// Which yanked versions are allowed
403    allowed_yanks: AllowedYanks,
404    /// The hashes of allowed distributions.
405    hasher: HashStrategy,
406    /// The `requires-python` constraint for the resolution.
407    requires_python: RequiresPython,
408}
409
410impl VersionMapLazy {
411    /// Returns the distribution for the given version, if it exists.
412    fn get(&self, version: &Version) -> Option<&PrioritizedDist> {
413        let lazy_dist = self.map.get(version)?;
414        let priority_dist = self.get_lazy(lazy_dist)?;
415        Some(priority_dist)
416    }
417
418    /// Given a reference to a possibly-initialized distribution that is in
419    /// this lazy map, return the corresponding distribution.
420    ///
421    /// When both a flat and simple distribution are present internally, they
422    /// are merged automatically.
423    fn get_lazy<'p>(&'p self, lazy_dist: &'p LazyPrioritizedDist) -> Option<&'p PrioritizedDist> {
424        match *lazy_dist {
425            LazyPrioritizedDist::OnlyFlat(ref dist) => Some(dist),
426            LazyPrioritizedDist::OnlySimple(ref dist) => self.get_simple(None, dist),
427            LazyPrioritizedDist::Both {
428                ref flat,
429                ref simple,
430            } => self.get_simple(Some(flat), simple),
431        }
432    }
433
434    /// Given an optional starting point, return the final form of the
435    /// given simple distribution. If it wasn't initialized yet, then this
436    /// initializes it. If the distribution would otherwise be empty, this
437    /// returns `None`.
438    fn get_simple<'p>(
439        &'p self,
440        init: Option<&'p PrioritizedDist>,
441        simple: &'p SimplePrioritizedDist,
442    ) -> Option<&'p PrioritizedDist> {
443        let get_or_init = || {
444            let files = rkyv::deserialize::<VersionFiles, rkyv::rancor::Error>(
445                &self
446                    .simple_metadata
447                    .datum(simple.datum_index)
448                    .expect("index to lazy dist is correct")
449                    .files,
450            )
451            .expect("archived version files always deserializes");
452            let mut priority_dist = init.cloned().unwrap_or_default();
453            for (filename, file) in files.all() {
454                // Support resolving as if it were an earlier timestamp, at least as long files have
455                // upload time information.
456                let (excluded, upload_time) = if let Some(exclude_newer) = &self.exclude_newer {
457                    match file.upload_time_utc_ms.as_ref() {
458                        Some(&upload_time) if upload_time >= exclude_newer.timestamp_millis() => {
459                            trace!(
460                                "Excluding `{}` (uploaded {upload_time}) due to exclude-newer ({exclude_newer})",
461                                file.filename
462                            );
463                            (true, Some(upload_time))
464                        }
465                        None => {
466                            warn_user_once!(
467                                "{} is missing an upload date, but user provided: {exclude_newer}",
468                                file.filename,
469                            );
470                            (true, None)
471                        }
472                        _ => (false, None),
473                    }
474                } else {
475                    (false, None)
476                };
477
478                // Prioritize amongst all available files.
479                let yanked = file.yanked.as_deref();
480                let hashes = file.hashes.clone();
481                match filename {
482                    DistFilename::WheelFilename(filename) => {
483                        let compatibility = self.wheel_compatibility(
484                            &filename,
485                            &filename.name,
486                            &filename.version,
487                            hashes.as_slice(),
488                            yanked,
489                            excluded,
490                            upload_time,
491                        );
492                        let dist = RegistryBuiltWheel {
493                            filename,
494                            file: Box::new(file),
495                            index: self.index.clone(),
496                        };
497                        priority_dist.insert_built(dist, hashes, compatibility);
498                    }
499                    DistFilename::SourceDistFilename(filename) => {
500                        let compatibility = self.source_dist_compatibility(
501                            &filename.name,
502                            &filename.version,
503                            hashes.as_slice(),
504                            yanked,
505                            excluded,
506                            upload_time,
507                        );
508                        let dist = RegistrySourceDist {
509                            name: filename.name.clone(),
510                            version: filename.version.clone(),
511                            ext: filename.extension,
512                            file: Box::new(file),
513                            index: self.index.clone(),
514                            wheels: vec![],
515                        };
516                        priority_dist.insert_source(dist, hashes, compatibility);
517                    }
518                }
519            }
520            if priority_dist.is_empty() {
521                None
522            } else {
523                Some(priority_dist)
524            }
525        };
526        simple.dist.get_or_init(get_or_init).as_ref()
527    }
528
529    fn source_dist_compatibility(
530        &self,
531        name: &PackageName,
532        version: &Version,
533        hashes: &[HashDigest],
534        yanked: Option<&Yanked>,
535        excluded: bool,
536        upload_time: Option<i64>,
537    ) -> SourceDistCompatibility {
538        // Check if builds are disabled
539        if self.no_build {
540            return SourceDistCompatibility::Incompatible(IncompatibleSource::NoBuild);
541        }
542
543        // Check if after upload time cutoff
544        if excluded {
545            return SourceDistCompatibility::Incompatible(IncompatibleSource::ExcludeNewer(
546                upload_time,
547            ));
548        }
549
550        // Check if yanked
551        if let Some(yanked) = yanked {
552            if yanked.is_yanked() && !self.allowed_yanks.contains(name, version) {
553                return SourceDistCompatibility::Incompatible(IncompatibleSource::Yanked(
554                    yanked.clone(),
555                ));
556            }
557        }
558
559        // Check if hashes line up. If hashes aren't required, they're considered matching.
560        let hash_policy = self.hasher.get_package(name, version);
561        let required_hashes = hash_policy.digests();
562        let hash = if required_hashes.is_empty() {
563            HashComparison::Matched
564        } else {
565            if hashes.is_empty() {
566                HashComparison::Missing
567            } else if hash_policy.matches(hashes) {
568                HashComparison::Matched
569            } else {
570                HashComparison::Mismatched
571            }
572        };
573
574        SourceDistCompatibility::Compatible(hash)
575    }
576
577    fn wheel_compatibility(
578        &self,
579        filename: &WheelFilename,
580        name: &PackageName,
581        version: &Version,
582        hashes: &[HashDigest],
583        yanked: Option<&Yanked>,
584        excluded: bool,
585        upload_time: Option<i64>,
586    ) -> WheelCompatibility {
587        // Check if binaries are disabled
588        if self.no_binary {
589            return WheelCompatibility::Incompatible(IncompatibleWheel::NoBinary);
590        }
591
592        // Check if after upload time cutoff
593        if excluded {
594            return WheelCompatibility::Incompatible(IncompatibleWheel::ExcludeNewer(upload_time));
595        }
596
597        // Check if yanked
598        if let Some(yanked) = yanked {
599            if yanked.is_yanked() && !self.allowed_yanks.contains(name, version) {
600                return WheelCompatibility::Incompatible(IncompatibleWheel::Yanked(yanked.clone()));
601            }
602        }
603
604        // Determine a compatibility for the wheel based on tags.
605        let priority = if let Some(tags) = &self.tags {
606            match filename.compatibility(tags) {
607                TagCompatibility::Incompatible(tag) => {
608                    return WheelCompatibility::Incompatible(IncompatibleWheel::Tag(tag));
609                }
610                TagCompatibility::Compatible(priority) => Some(priority),
611            }
612        } else {
613            // Check if the wheel is compatible with the `requires-python` (i.e., the Python
614            // ABI tag is not less than the `requires-python` minimum version).
615            if !self.requires_python.matches_wheel_tag(filename) {
616                return WheelCompatibility::Incompatible(IncompatibleWheel::Tag(
617                    IncompatibleTag::AbiPythonVersion,
618                ));
619            }
620            None
621        };
622
623        // Check if hashes line up. If hashes aren't required, they're considered matching.
624        let hash_policy = self.hasher.get_package(name, version);
625        let required_hashes = hash_policy.digests();
626        let hash = if required_hashes.is_empty() {
627            HashComparison::Matched
628        } else {
629            if hashes.is_empty() {
630                HashComparison::Missing
631            } else if hash_policy.matches(hashes) {
632                HashComparison::Matched
633            } else {
634                HashComparison::Mismatched
635            }
636        };
637
638        // Break ties with the build tag.
639        let build_tag = filename.build_tag().cloned();
640
641        WheelCompatibility::Compatible(hash, priority, build_tag)
642    }
643}
644
645/// Represents a possibly initialized [`PrioritizedDist`] for
646/// a single version of a package.
647#[derive(Debug)]
648enum LazyPrioritizedDist {
649    /// Represents an eagerly constructed distribution from a
650    /// `FlatDistributions`.
651    OnlyFlat(PrioritizedDist),
652    /// Represents a lazily constructed distribution from an index into a
653    /// `VersionFiles` from `SimpleDetailMetadata`.
654    OnlySimple(SimplePrioritizedDist),
655    /// Combines the above. This occurs when we have data from both a flat
656    /// distribution and a simple distribution.
657    Both {
658        flat: PrioritizedDist,
659        simple: SimplePrioritizedDist,
660    },
661}
662
663/// Represents a lazily initialized `PrioritizedDist`.
664#[derive(Debug)]
665struct SimplePrioritizedDist {
666    /// An offset into `SimpleDetailMetadata` corresponding to a `SimpleMetadatum`.
667    /// This provides access to a `VersionFiles` that is used to construct a
668    /// `PrioritizedDist`.
669    datum_index: usize,
670    /// A lazily initialized distribution.
671    ///
672    /// Note that the `Option` does not represent the initialization state.
673    /// The `Option` can be `None` even after initialization, for example,
674    /// if initialization could not find any usable files from which to
675    /// construct a distribution. (One easy way to effect this, at the time
676    /// of writing, is to use `--exclude-newer 1900-01-01`.)
677    dist: OnceLock<Option<PrioritizedDist>>,
678}
679
680/// A range that can be used to iterate over a subset of a [`BTreeMap`].
681#[derive(Debug)]
682struct BoundingRange<'a> {
683    min: Bound<&'a Version>,
684    max: Bound<&'a Version>,
685}
686
687impl<'a> From<&'a Ranges<Version>> for BoundingRange<'a> {
688    fn from(value: &'a Ranges<Version>) -> Self {
689        let (min, max) = value
690            .bounding_range()
691            .unwrap_or((Bound::Unbounded, Bound::Unbounded));
692        Self { min, max }
693    }
694}
695
696impl<'a> RangeBounds<Version> for BoundingRange<'a> {
697    fn start_bound(&self) -> Bound<&'a Version> {
698        self.min
699    }
700
701    fn end_bound(&self) -> Bound<&'a Version> {
702        self.max
703    }
704}