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;
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, ExcludeNewerTimestamp, 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#[allow(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" `SimpleMetadata` 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 `SimpleMetadata` 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<ExcludeNewerTimestamp>,
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                            (true, Some(upload_time))
452                        }
453                        None => {
454                            warn_user_once!(
455                                "{} is missing an upload date, but user provided: {exclude_newer}",
456                                file.filename,
457                            );
458                            (true, None)
459                        }
460                        _ => (false, None),
461                    }
462                } else {
463                    (false, None)
464                };
465
466                // Prioritize amongst all available files.
467                let yanked = file.yanked.as_deref();
468                let hashes = file.hashes.clone();
469                match filename {
470                    DistFilename::WheelFilename(filename) => {
471                        let compatibility = self.wheel_compatibility(
472                            &filename,
473                            &filename.name,
474                            &filename.version,
475                            hashes.as_slice(),
476                            yanked,
477                            excluded,
478                            upload_time,
479                        );
480                        let dist = RegistryBuiltWheel {
481                            filename,
482                            file: Box::new(file),
483                            index: self.index.clone(),
484                        };
485                        priority_dist.insert_built(dist, hashes, compatibility);
486                    }
487                    DistFilename::SourceDistFilename(filename) => {
488                        let compatibility = self.source_dist_compatibility(
489                            &filename.name,
490                            &filename.version,
491                            hashes.as_slice(),
492                            yanked,
493                            excluded,
494                            upload_time,
495                        );
496                        let dist = RegistrySourceDist {
497                            name: filename.name.clone(),
498                            version: filename.version.clone(),
499                            ext: filename.extension,
500                            file: Box::new(file),
501                            index: self.index.clone(),
502                            wheels: vec![],
503                        };
504                        priority_dist.insert_source(dist, hashes, compatibility);
505                    }
506                }
507            }
508            if priority_dist.is_empty() {
509                None
510            } else {
511                Some(priority_dist)
512            }
513        };
514        simple.dist.get_or_init(get_or_init).as_ref()
515    }
516
517    fn source_dist_compatibility(
518        &self,
519        name: &PackageName,
520        version: &Version,
521        hashes: &[HashDigest],
522        yanked: Option<&Yanked>,
523        excluded: bool,
524        upload_time: Option<i64>,
525    ) -> SourceDistCompatibility {
526        // Check if builds are disabled
527        if self.no_build {
528            return SourceDistCompatibility::Incompatible(IncompatibleSource::NoBuild);
529        }
530
531        // Check if after upload time cutoff
532        if excluded {
533            return SourceDistCompatibility::Incompatible(IncompatibleSource::ExcludeNewer(
534                upload_time,
535            ));
536        }
537
538        // Check if yanked
539        if let Some(yanked) = yanked {
540            if yanked.is_yanked() && !self.allowed_yanks.contains(name, version) {
541                return SourceDistCompatibility::Incompatible(IncompatibleSource::Yanked(
542                    yanked.clone(),
543                ));
544            }
545        }
546
547        // Check if hashes line up. If hashes aren't required, they're considered matching.
548        let hash_policy = self.hasher.get_package(name, version);
549        let required_hashes = hash_policy.digests();
550        let hash = if required_hashes.is_empty() {
551            HashComparison::Matched
552        } else {
553            if hashes.is_empty() {
554                HashComparison::Missing
555            } else if hashes.iter().any(|hash| required_hashes.contains(hash)) {
556                HashComparison::Matched
557            } else {
558                HashComparison::Mismatched
559            }
560        };
561
562        SourceDistCompatibility::Compatible(hash)
563    }
564
565    fn wheel_compatibility(
566        &self,
567        filename: &WheelFilename,
568        name: &PackageName,
569        version: &Version,
570        hashes: &[HashDigest],
571        yanked: Option<&Yanked>,
572        excluded: bool,
573        upload_time: Option<i64>,
574    ) -> WheelCompatibility {
575        // Check if binaries are disabled
576        if self.no_binary {
577            return WheelCompatibility::Incompatible(IncompatibleWheel::NoBinary);
578        }
579
580        // Check if after upload time cutoff
581        if excluded {
582            return WheelCompatibility::Incompatible(IncompatibleWheel::ExcludeNewer(upload_time));
583        }
584
585        // Check if yanked
586        if let Some(yanked) = yanked {
587            if yanked.is_yanked() && !self.allowed_yanks.contains(name, version) {
588                return WheelCompatibility::Incompatible(IncompatibleWheel::Yanked(yanked.clone()));
589            }
590        }
591
592        // Determine a compatibility for the wheel based on tags.
593        let priority = if let Some(tags) = &self.tags {
594            match filename.compatibility(tags) {
595                TagCompatibility::Incompatible(tag) => {
596                    return WheelCompatibility::Incompatible(IncompatibleWheel::Tag(tag));
597                }
598                TagCompatibility::Compatible(priority) => Some(priority),
599            }
600        } else {
601            // Check if the wheel is compatible with the `requires-python` (i.e., the Python
602            // ABI tag is not less than the `requires-python` minimum version).
603            if !self.requires_python.matches_wheel_tag(filename) {
604                return WheelCompatibility::Incompatible(IncompatibleWheel::Tag(
605                    IncompatibleTag::AbiPythonVersion,
606                ));
607            }
608            None
609        };
610
611        // Check if hashes line up. If hashes aren't required, they're considered matching.
612        let hash_policy = self.hasher.get_package(name, version);
613        let required_hashes = hash_policy.digests();
614        let hash = if required_hashes.is_empty() {
615            HashComparison::Matched
616        } else {
617            if hashes.is_empty() {
618                HashComparison::Missing
619            } else if hashes.iter().any(|hash| required_hashes.contains(hash)) {
620                HashComparison::Matched
621            } else {
622                HashComparison::Mismatched
623            }
624        };
625
626        // Break ties with the build tag.
627        let build_tag = filename.build_tag().cloned();
628
629        WheelCompatibility::Compatible(hash, priority, build_tag)
630    }
631}
632
633/// Represents a possibly initialized [`PrioritizedDist`] for
634/// a single version of a package.
635#[derive(Debug)]
636enum LazyPrioritizedDist {
637    /// Represents an eagerly constructed distribution from a
638    /// `FlatDistributions`.
639    OnlyFlat(PrioritizedDist),
640    /// Represents a lazily constructed distribution from an index into a
641    /// `VersionFiles` from `SimpleMetadata`.
642    OnlySimple(SimplePrioritizedDist),
643    /// Combines the above. This occurs when we have data from both a flat
644    /// distribution and a simple distribution.
645    Both {
646        flat: PrioritizedDist,
647        simple: SimplePrioritizedDist,
648    },
649}
650
651/// Represents a lazily initialized `PrioritizedDist`.
652#[derive(Debug)]
653struct SimplePrioritizedDist {
654    /// An offset into `SimpleMetadata` corresponding to a `SimpleMetadatum`.
655    /// This provides access to a `VersionFiles` that is used to construct a
656    /// `PrioritizedDist`.
657    datum_index: usize,
658    /// A lazily initialized distribution.
659    ///
660    /// Note that the `Option` does not represent the initialization state.
661    /// The `Option` can be `None` even after initialization, for example,
662    /// if initialization could not find any usable files from which to
663    /// construct a distribution. (One easy way to effect this, at the time
664    /// of writing, is to use `--exclude-newer 1900-01-01`.)
665    dist: OnceLock<Option<PrioritizedDist>>,
666}
667
668/// A range that can be used to iterate over a subset of a [`BTreeMap`].
669#[derive(Debug)]
670struct BoundingRange<'a> {
671    min: Bound<&'a Version>,
672    max: Bound<&'a Version>,
673}
674
675impl<'a> From<&'a Ranges<Version>> for BoundingRange<'a> {
676    fn from(value: &'a Ranges<Version>) -> Self {
677        let (min, max) = value
678            .bounding_range()
679            .unwrap_or((Bound::Unbounded, Bound::Unbounded));
680        Self { min, max }
681    }
682}
683
684impl<'a> RangeBounds<Version> for BoundingRange<'a> {
685    fn start_bound(&self) -> Bound<&'a Version> {
686        self.min
687    }
688
689    fn end_bound(&self) -> Bound<&'a Version> {
690        self.max
691    }
692}