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::{ExcludeNewer, 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<&ExcludeNewer>,
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: exclude_newer.and_then(|en| en.exclude_newer_package(package_name)),
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 an iterator over the versions and distributions.
192    ///
193    /// Note that the value returned in this iterator is a [`VersionMapDist`],
194    /// which can be used to lazily request a [`CompatibleDist`]. This is
195    /// useful in cases where one can skip materializing a full distribution
196    /// for each version.
197    pub(crate) fn iter(
198        &self,
199        range: &Ranges<Version>,
200    ) -> impl DoubleEndedIterator<Item = (&Version, VersionMapDistHandle<'_>)> {
201        // Performance optimization: If we only have a single version, return that version directly.
202        if let Some(version) = range.as_singleton() {
203            either::Either::Left(match self.inner {
204                VersionMapInner::Eager(ref eager) => {
205                    either::Either::Left(eager.map.get_key_value(version).into_iter().map(
206                        move |(version, dist)| {
207                            let version_map_dist = VersionMapDistHandle {
208                                inner: VersionMapDistHandleInner::Eager(dist),
209                            };
210                            (version, version_map_dist)
211                        },
212                    ))
213                }
214                VersionMapInner::Lazy(ref lazy) => {
215                    either::Either::Right(lazy.map.get_key_value(version).into_iter().map(
216                        move |(version, dist)| {
217                            let version_map_dist = VersionMapDistHandle {
218                                inner: VersionMapDistHandleInner::Lazy { lazy, dist },
219                            };
220                            (version, version_map_dist)
221                        },
222                    ))
223                }
224            })
225        } else {
226            either::Either::Right(match self.inner {
227                VersionMapInner::Eager(ref eager) => {
228                    either::Either::Left(eager.map.range(BoundingRange::from(range)).map(
229                        |(version, dist)| {
230                            let version_map_dist = VersionMapDistHandle {
231                                inner: VersionMapDistHandleInner::Eager(dist),
232                            };
233                            (version, version_map_dist)
234                        },
235                    ))
236                }
237                VersionMapInner::Lazy(ref lazy) => {
238                    either::Either::Right(lazy.map.range(BoundingRange::from(range)).map(
239                        |(version, dist)| {
240                            let version_map_dist = VersionMapDistHandle {
241                                inner: VersionMapDistHandleInner::Lazy { lazy, dist },
242                            };
243                            (version, version_map_dist)
244                        },
245                    ))
246                }
247            })
248        }
249    }
250
251    /// Return the [`Hashes`] for the given version, if any.
252    pub(crate) fn hashes(&self, version: &Version) -> Option<&[HashDigest]> {
253        match self.inner {
254            VersionMapInner::Eager(ref eager) => {
255                eager.map.get(version).map(PrioritizedDist::hashes)
256            }
257            VersionMapInner::Lazy(ref lazy) => lazy.get(version).map(PrioritizedDist::hashes),
258        }
259    }
260
261    /// Returns the total number of distinct versions in this map.
262    ///
263    /// Note that this may include versions of distributions that are not
264    /// usable in the current environment.
265    pub(crate) fn len(&self) -> usize {
266        match self.inner {
267            VersionMapInner::Eager(VersionMapEager { ref map, .. }) => map.len(),
268            VersionMapInner::Lazy(VersionMapLazy { ref map, .. }) => map.len(),
269        }
270    }
271
272    /// Returns `true` if the map contains at least one stable (non-pre-release) version.
273    pub(crate) fn stable(&self) -> bool {
274        match self.inner {
275            VersionMapInner::Eager(ref map) => map.stable,
276            VersionMapInner::Lazy(ref map) => map.stable,
277        }
278    }
279
280    /// Returns `true` if the map contains at least one local version (e.g., `2.6.0+cpu`).
281    pub(crate) fn local(&self) -> bool {
282        match self.inner {
283            VersionMapInner::Eager(ref map) => map.local,
284            VersionMapInner::Lazy(ref map) => map.local,
285        }
286    }
287}
288
289impl From<FlatDistributions> for VersionMap {
290    fn from(flat_index: FlatDistributions) -> Self {
291        let stable = flat_index.iter().any(|(version, _)| version.is_stable());
292        let local = flat_index.iter().any(|(version, _)| version.is_local());
293        let map = flat_index.into();
294        Self {
295            inner: VersionMapInner::Eager(VersionMapEager { map, stable, local }),
296        }
297    }
298}
299
300/// A lazily initialized distribution.
301///
302/// This permits access to a handle that can be turned into a resolvable
303/// distribution when desired. This is coupled with a `Version` in
304/// [`VersionMap::iter`] to permit iteration over all items in a map without
305/// necessarily constructing a distribution for every version if it isn't
306/// needed.
307///
308/// Note that because of laziness, not all such items can be turned into
309/// a valid distribution. For example, if in the process of building a
310/// distribution no compatible wheel or source distribution could be found,
311/// then building a `CompatibleDist` will fail.
312pub(crate) struct VersionMapDistHandle<'a> {
313    inner: VersionMapDistHandleInner<'a>,
314}
315
316enum VersionMapDistHandleInner<'a> {
317    Eager(&'a PrioritizedDist),
318    Lazy {
319        lazy: &'a VersionMapLazy,
320        dist: &'a LazyPrioritizedDist,
321    },
322}
323
324impl<'a> VersionMapDistHandle<'a> {
325    /// Returns a prioritized distribution from this handle.
326    pub(crate) fn prioritized_dist(&self) -> Option<&'a PrioritizedDist> {
327        match self.inner {
328            VersionMapDistHandleInner::Eager(dist) => Some(dist),
329            VersionMapDistHandleInner::Lazy { lazy, dist } => Some(lazy.get_lazy(dist)?),
330        }
331    }
332}
333
334/// The kind of internal version map we have.
335#[derive(Debug)]
336#[expect(clippy::large_enum_variant)]
337enum VersionMapInner {
338    /// All distributions are fully materialized in memory.
339    ///
340    /// This usually happens when one needs a `VersionMap` from a
341    /// `FlatDistributions`.
342    Eager(VersionMapEager),
343    /// Some distributions might be fully materialized (i.e., by initializing
344    /// a `VersionMap` with a `FlatDistributions`), but some distributions
345    /// might still be in their "raw" `SimpleDetailMetadata` format. In this case, a
346    /// `PrioritizedDist` isn't actually created in memory until the
347    /// specific version has been requested.
348    Lazy(VersionMapLazy),
349}
350
351/// A map from versions to distributions that are fully materialized in memory.
352#[derive(Debug)]
353struct VersionMapEager {
354    /// A map from version to distribution.
355    map: BTreeMap<Version, PrioritizedDist>,
356    /// Whether the version map contains at least one stable (non-pre-release) version.
357    stable: bool,
358    /// Whether the version map contains at least one local version.
359    local: bool,
360}
361
362/// A map that lazily materializes some prioritized distributions upon access.
363///
364/// The idea here is that some packages have a lot of versions published, and
365/// needing to materialize a full `VersionMap` with all corresponding metadata
366/// for every version in memory is expensive. Since a `SimpleDetailMetadata` can be
367/// materialized with very little cost (via `rkyv` in the warm cached case),
368/// avoiding another conversion step into a fully filled out `VersionMap` can
369/// provide substantial savings in some cases.
370#[derive(Debug)]
371struct VersionMapLazy {
372    /// A map from version to possibly-initialized distribution.
373    map: BTreeMap<Version, LazyPrioritizedDist>,
374    /// Whether the version map contains at least one stable (non-pre-release) version.
375    stable: bool,
376    /// Whether the version map contains at least one local version.
377    local: bool,
378    /// The pre-populated metadata for each version.
379    core_metadata: FxHashMap<Version, ResolutionMetadata>,
380    /// The raw simple metadata from which `PrioritizedDist`s should
381    /// be constructed.
382    simple_metadata: OwnedArchive<SimpleDetailMetadata>,
383    /// When true, wheels aren't allowed.
384    no_binary: bool,
385    /// When true, source dists aren't allowed.
386    no_build: bool,
387    /// The URL of the index where this package came from.
388    index: IndexUrl,
389    /// The set of compatibility tags that determines whether a wheel is usable
390    /// in the current environment.
391    tags: Option<Tags>,
392    /// Whether files newer than this timestamp should be excluded or not.
393    exclude_newer: Option<ExcludeNewerValue>,
394    /// Which yanked versions are allowed
395    allowed_yanks: AllowedYanks,
396    /// The hashes of allowed distributions.
397    hasher: HashStrategy,
398    /// The `requires-python` constraint for the resolution.
399    requires_python: RequiresPython,
400}
401
402impl VersionMapLazy {
403    /// Returns the distribution for the given version, if it exists.
404    fn get(&self, version: &Version) -> Option<&PrioritizedDist> {
405        let lazy_dist = self.map.get(version)?;
406        let priority_dist = self.get_lazy(lazy_dist)?;
407        Some(priority_dist)
408    }
409
410    /// Given a reference to a possibly-initialized distribution that is in
411    /// this lazy map, return the corresponding distribution.
412    ///
413    /// When both a flat and simple distribution are present internally, they
414    /// are merged automatically.
415    fn get_lazy<'p>(&'p self, lazy_dist: &'p LazyPrioritizedDist) -> Option<&'p PrioritizedDist> {
416        match *lazy_dist {
417            LazyPrioritizedDist::OnlyFlat(ref dist) => Some(dist),
418            LazyPrioritizedDist::OnlySimple(ref dist) => self.get_simple(None, dist),
419            LazyPrioritizedDist::Both {
420                ref flat,
421                ref simple,
422            } => self.get_simple(Some(flat), simple),
423        }
424    }
425
426    /// Given an optional starting point, return the final form of the
427    /// given simple distribution. If it wasn't initialized yet, then this
428    /// initializes it. If the distribution would otherwise be empty, this
429    /// returns `None`.
430    fn get_simple<'p>(
431        &'p self,
432        init: Option<&'p PrioritizedDist>,
433        simple: &'p SimplePrioritizedDist,
434    ) -> Option<&'p PrioritizedDist> {
435        let get_or_init = || {
436            let files = rkyv::deserialize::<VersionFiles, rkyv::rancor::Error>(
437                &self
438                    .simple_metadata
439                    .datum(simple.datum_index)
440                    .expect("index to lazy dist is correct")
441                    .files,
442            )
443            .expect("archived version files always deserializes");
444            let mut priority_dist = init.cloned().unwrap_or_default();
445            for (filename, file) in files.all() {
446                // Support resolving as if it were an earlier timestamp, at least as long files have
447                // upload time information.
448                let (excluded, upload_time) = if let Some(exclude_newer) = &self.exclude_newer {
449                    match file.upload_time_utc_ms.as_ref() {
450                        Some(&upload_time) if upload_time >= exclude_newer.timestamp_millis() => {
451                            trace!(
452                                "Excluding `{}` (uploaded {upload_time}) due to exclude-newer ({exclude_newer})",
453                                file.filename
454                            );
455                            (true, Some(upload_time))
456                        }
457                        None => {
458                            warn_user_once!(
459                                "{} is missing an upload date, but user provided: {exclude_newer}",
460                                file.filename,
461                            );
462                            (true, None)
463                        }
464                        _ => (false, None),
465                    }
466                } else {
467                    (false, None)
468                };
469
470                // Prioritize amongst all available files.
471                let yanked = file.yanked.as_deref();
472                let hashes = file.hashes.clone();
473                match filename {
474                    DistFilename::WheelFilename(filename) => {
475                        let compatibility = self.wheel_compatibility(
476                            &filename,
477                            &filename.name,
478                            &filename.version,
479                            hashes.as_slice(),
480                            yanked,
481                            excluded,
482                            upload_time,
483                        );
484                        let dist = RegistryBuiltWheel {
485                            filename,
486                            file: Box::new(file),
487                            index: self.index.clone(),
488                        };
489                        priority_dist.insert_built(dist, hashes, compatibility);
490                    }
491                    DistFilename::SourceDistFilename(filename) => {
492                        let compatibility = self.source_dist_compatibility(
493                            &filename.name,
494                            &filename.version,
495                            hashes.as_slice(),
496                            yanked,
497                            excluded,
498                            upload_time,
499                        );
500                        let dist = RegistrySourceDist {
501                            name: filename.name.clone(),
502                            version: filename.version.clone(),
503                            ext: filename.extension,
504                            file: Box::new(file),
505                            index: self.index.clone(),
506                            wheels: vec![],
507                        };
508                        priority_dist.insert_source(dist, hashes, compatibility);
509                    }
510                }
511            }
512            if priority_dist.is_empty() {
513                None
514            } else {
515                Some(priority_dist)
516            }
517        };
518        simple.dist.get_or_init(get_or_init).as_ref()
519    }
520
521    fn source_dist_compatibility(
522        &self,
523        name: &PackageName,
524        version: &Version,
525        hashes: &[HashDigest],
526        yanked: Option<&Yanked>,
527        excluded: bool,
528        upload_time: Option<i64>,
529    ) -> SourceDistCompatibility {
530        // Check if builds are disabled
531        if self.no_build {
532            return SourceDistCompatibility::Incompatible(IncompatibleSource::NoBuild);
533        }
534
535        // Check if after upload time cutoff
536        if excluded {
537            return SourceDistCompatibility::Incompatible(IncompatibleSource::ExcludeNewer(
538                upload_time,
539            ));
540        }
541
542        // Check if yanked
543        if let Some(yanked) = yanked {
544            if yanked.is_yanked() && !self.allowed_yanks.contains(name, version) {
545                return SourceDistCompatibility::Incompatible(IncompatibleSource::Yanked(
546                    yanked.clone(),
547                ));
548            }
549        }
550
551        // Check if hashes line up. If hashes aren't required, they're considered matching.
552        let hash_policy = self.hasher.get_package(name, version);
553        let required_hashes = hash_policy.digests();
554        let hash = if required_hashes.is_empty() {
555            HashComparison::Matched
556        } else {
557            if hashes.is_empty() {
558                HashComparison::Missing
559            } else if hashes.iter().any(|hash| required_hashes.contains(hash)) {
560                HashComparison::Matched
561            } else {
562                HashComparison::Mismatched
563            }
564        };
565
566        SourceDistCompatibility::Compatible(hash)
567    }
568
569    fn wheel_compatibility(
570        &self,
571        filename: &WheelFilename,
572        name: &PackageName,
573        version: &Version,
574        hashes: &[HashDigest],
575        yanked: Option<&Yanked>,
576        excluded: bool,
577        upload_time: Option<i64>,
578    ) -> WheelCompatibility {
579        // Check if binaries are disabled
580        if self.no_binary {
581            return WheelCompatibility::Incompatible(IncompatibleWheel::NoBinary);
582        }
583
584        // Check if after upload time cutoff
585        if excluded {
586            return WheelCompatibility::Incompatible(IncompatibleWheel::ExcludeNewer(upload_time));
587        }
588
589        // Check if yanked
590        if let Some(yanked) = yanked {
591            if yanked.is_yanked() && !self.allowed_yanks.contains(name, version) {
592                return WheelCompatibility::Incompatible(IncompatibleWheel::Yanked(yanked.clone()));
593            }
594        }
595
596        // Determine a compatibility for the wheel based on tags.
597        let priority = if let Some(tags) = &self.tags {
598            match filename.compatibility(tags) {
599                TagCompatibility::Incompatible(tag) => {
600                    return WheelCompatibility::Incompatible(IncompatibleWheel::Tag(tag));
601                }
602                TagCompatibility::Compatible(priority) => Some(priority),
603            }
604        } else {
605            // Check if the wheel is compatible with the `requires-python` (i.e., the Python
606            // ABI tag is not less than the `requires-python` minimum version).
607            if !self.requires_python.matches_wheel_tag(filename) {
608                return WheelCompatibility::Incompatible(IncompatibleWheel::Tag(
609                    IncompatibleTag::AbiPythonVersion,
610                ));
611            }
612            None
613        };
614
615        // Check if hashes line up. If hashes aren't required, they're considered matching.
616        let hash_policy = self.hasher.get_package(name, version);
617        let required_hashes = hash_policy.digests();
618        let hash = if required_hashes.is_empty() {
619            HashComparison::Matched
620        } else {
621            if hashes.is_empty() {
622                HashComparison::Missing
623            } else if hashes.iter().any(|hash| required_hashes.contains(hash)) {
624                HashComparison::Matched
625            } else {
626                HashComparison::Mismatched
627            }
628        };
629
630        // Break ties with the build tag.
631        let build_tag = filename.build_tag().cloned();
632
633        WheelCompatibility::Compatible(hash, priority, build_tag)
634    }
635}
636
637/// Represents a possibly initialized [`PrioritizedDist`] for
638/// a single version of a package.
639#[derive(Debug)]
640enum LazyPrioritizedDist {
641    /// Represents an eagerly constructed distribution from a
642    /// `FlatDistributions`.
643    OnlyFlat(PrioritizedDist),
644    /// Represents a lazily constructed distribution from an index into a
645    /// `VersionFiles` from `SimpleDetailMetadata`.
646    OnlySimple(SimplePrioritizedDist),
647    /// Combines the above. This occurs when we have data from both a flat
648    /// distribution and a simple distribution.
649    Both {
650        flat: PrioritizedDist,
651        simple: SimplePrioritizedDist,
652    },
653}
654
655/// Represents a lazily initialized `PrioritizedDist`.
656#[derive(Debug)]
657struct SimplePrioritizedDist {
658    /// An offset into `SimpleDetailMetadata` corresponding to a `SimpleMetadatum`.
659    /// This provides access to a `VersionFiles` that is used to construct a
660    /// `PrioritizedDist`.
661    datum_index: usize,
662    /// A lazily initialized distribution.
663    ///
664    /// Note that the `Option` does not represent the initialization state.
665    /// The `Option` can be `None` even after initialization, for example,
666    /// if initialization could not find any usable files from which to
667    /// construct a distribution. (One easy way to effect this, at the time
668    /// of writing, is to use `--exclude-newer 1900-01-01`.)
669    dist: OnceLock<Option<PrioritizedDist>>,
670}
671
672/// A range that can be used to iterate over a subset of a [`BTreeMap`].
673#[derive(Debug)]
674struct BoundingRange<'a> {
675    min: Bound<&'a Version>,
676    max: Bound<&'a Version>,
677}
678
679impl<'a> From<&'a Ranges<Version>> for BoundingRange<'a> {
680    fn from(value: &'a Ranges<Version>) -> Self {
681        let (min, max) = value
682            .bounding_range()
683            .unwrap_or((Bound::Unbounded, Bound::Unbounded));
684        Self { min, max }
685    }
686}
687
688impl<'a> RangeBounds<Version> for BoundingRange<'a> {
689    fn start_bound(&self) -> Bound<&'a Version> {
690        self.min
691    }
692
693    fn end_bound(&self) -> Bound<&'a Version> {
694        self.max
695    }
696}