uv_distribution_types/
resolved.rs

1use std::fmt::{Display, Formatter};
2use std::path::Path;
3use std::sync::Arc;
4
5use uv_normalize::PackageName;
6use uv_pep440::Version;
7use uv_pypi_types::Yanked;
8
9use crate::{
10    BuiltDist, Dist, DistributionId, DistributionMetadata, Identifier, IndexUrl, InstalledDist,
11    Name, PrioritizedDist, RegistryBuiltWheel, RegistrySourceDist, ResourceId, SourceDist,
12    VersionOrUrlRef,
13};
14
15/// A distribution that can be used for resolution and installation.
16///
17/// Either an already-installed distribution or a distribution that can be installed.
18#[derive(Debug, Clone, Hash)]
19#[allow(clippy::large_enum_variant)]
20pub enum ResolvedDist {
21    Installed {
22        dist: Arc<InstalledDist>,
23    },
24    Installable {
25        dist: Arc<Dist>,
26        version: Option<Version>,
27    },
28}
29
30/// A variant of [`ResolvedDist`] with borrowed inner distributions.
31#[derive(Debug, Clone)]
32pub enum ResolvedDistRef<'a> {
33    Installed {
34        dist: &'a InstalledDist,
35    },
36    InstallableRegistrySourceDist {
37        /// The source distribution that should be used.
38        sdist: &'a RegistrySourceDist,
39        /// The prioritized distribution that the wheel came from.
40        prioritized: &'a PrioritizedDist,
41    },
42    InstallableRegistryBuiltDist {
43        /// The wheel that should be used.
44        wheel: &'a RegistryBuiltWheel,
45        /// The prioritized distribution that the wheel came from.
46        prioritized: &'a PrioritizedDist,
47    },
48}
49
50impl ResolvedDist {
51    /// Return true if the distribution is editable.
52    pub fn is_editable(&self) -> bool {
53        match self {
54            Self::Installable { dist, .. } => dist.is_editable(),
55            Self::Installed { dist } => dist.is_editable(),
56        }
57    }
58
59    /// Return true if the distribution refers to a local file or directory.
60    pub fn is_local(&self) -> bool {
61        match self {
62            Self::Installable { dist, .. } => dist.is_local(),
63            Self::Installed { dist } => dist.is_local(),
64        }
65    }
66
67    /// Returns the [`IndexUrl`], if the distribution is from a registry.
68    pub fn index(&self) -> Option<&IndexUrl> {
69        match self {
70            Self::Installable { dist, .. } => dist.index(),
71            Self::Installed { .. } => None,
72        }
73    }
74
75    /// Returns the [`Yanked`] status of the distribution, if available.
76    pub fn yanked(&self) -> Option<&Yanked> {
77        match self {
78            Self::Installable { dist, .. } => match dist.as_ref() {
79                Dist::Source(SourceDist::Registry(sdist)) => sdist.file.yanked.as_deref(),
80                Dist::Built(BuiltDist::Registry(wheel)) => {
81                    wheel.best_wheel().file.yanked.as_deref()
82                }
83                _ => None,
84            },
85            Self::Installed { .. } => None,
86        }
87    }
88
89    /// Returns the version of the distribution, if available.
90    pub fn version(&self) -> Option<&Version> {
91        match self {
92            Self::Installable { version, dist } => dist.version().or(version.as_ref()),
93            Self::Installed { dist } => Some(dist.version()),
94        }
95    }
96
97    /// Return the source tree of the distribution, if available.
98    pub fn source_tree(&self) -> Option<&Path> {
99        match self {
100            Self::Installable { dist, .. } => dist.source_tree(),
101            Self::Installed { .. } => None,
102        }
103    }
104}
105
106impl ResolvedDistRef<'_> {
107    pub fn to_owned(&self) -> ResolvedDist {
108        match self {
109            Self::InstallableRegistrySourceDist { sdist, prioritized } => {
110                // This is okay because we're only here if the prioritized dist
111                // has an sdist, so this always succeeds.
112                let source = prioritized.source_dist().expect("a source distribution");
113                assert_eq!(
114                    (&sdist.name, &sdist.version),
115                    (&source.name, &source.version),
116                    "expected chosen sdist to match prioritized sdist"
117                );
118                ResolvedDist::Installable {
119                    dist: Arc::new(Dist::Source(SourceDist::Registry(source))),
120                    version: Some(sdist.version.clone()),
121                }
122            }
123            Self::InstallableRegistryBuiltDist {
124                wheel, prioritized, ..
125            } => {
126                assert_eq!(
127                    Some(&wheel.filename),
128                    prioritized.best_wheel().map(|(wheel, _)| &wheel.filename),
129                    "expected chosen wheel to match best wheel"
130                );
131                // This is okay because we're only here if the prioritized dist
132                // has at least one wheel, so this always succeeds.
133                let built = prioritized.built_dist().expect("at least one wheel");
134                ResolvedDist::Installable {
135                    dist: Arc::new(Dist::Built(BuiltDist::Registry(built))),
136                    version: Some(wheel.filename.version.clone()),
137                }
138            }
139            Self::Installed { dist } => ResolvedDist::Installed {
140                dist: Arc::new((*dist).clone()),
141            },
142        }
143    }
144
145    /// Returns the [`IndexUrl`], if the distribution is from a registry.
146    pub fn index(&self) -> Option<&IndexUrl> {
147        match self {
148            Self::InstallableRegistrySourceDist { sdist, .. } => Some(&sdist.index),
149            Self::InstallableRegistryBuiltDist { wheel, .. } => Some(&wheel.index),
150            Self::Installed { .. } => None,
151        }
152    }
153}
154
155impl Display for ResolvedDistRef<'_> {
156    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
157        match self {
158            Self::InstallableRegistrySourceDist { sdist, .. } => Display::fmt(sdist, f),
159            Self::InstallableRegistryBuiltDist { wheel, .. } => Display::fmt(wheel, f),
160            Self::Installed { dist } => Display::fmt(dist, f),
161        }
162    }
163}
164
165impl Name for ResolvedDistRef<'_> {
166    fn name(&self) -> &PackageName {
167        match self {
168            Self::InstallableRegistrySourceDist { sdist, .. } => sdist.name(),
169            Self::InstallableRegistryBuiltDist { wheel, .. } => wheel.name(),
170            Self::Installed { dist } => dist.name(),
171        }
172    }
173}
174
175impl DistributionMetadata for ResolvedDistRef<'_> {
176    fn version_or_url(&self) -> VersionOrUrlRef<'_> {
177        match self {
178            Self::Installed { dist } => VersionOrUrlRef::Version(dist.version()),
179            Self::InstallableRegistrySourceDist { sdist, .. } => sdist.version_or_url(),
180            Self::InstallableRegistryBuiltDist { wheel, .. } => wheel.version_or_url(),
181        }
182    }
183}
184
185impl Identifier for ResolvedDistRef<'_> {
186    fn distribution_id(&self) -> DistributionId {
187        match self {
188            Self::Installed { dist } => dist.distribution_id(),
189            Self::InstallableRegistrySourceDist { sdist, .. } => sdist.distribution_id(),
190            Self::InstallableRegistryBuiltDist { wheel, .. } => wheel.distribution_id(),
191        }
192    }
193
194    fn resource_id(&self) -> ResourceId {
195        match self {
196            Self::Installed { dist } => dist.resource_id(),
197            Self::InstallableRegistrySourceDist { sdist, .. } => sdist.resource_id(),
198            Self::InstallableRegistryBuiltDist { wheel, .. } => wheel.resource_id(),
199        }
200    }
201}
202
203impl Name for ResolvedDist {
204    fn name(&self) -> &PackageName {
205        match self {
206            Self::Installable { dist, .. } => dist.name(),
207            Self::Installed { dist } => dist.name(),
208        }
209    }
210}
211
212impl DistributionMetadata for ResolvedDist {
213    fn version_or_url(&self) -> VersionOrUrlRef<'_> {
214        match self {
215            Self::Installed { dist } => dist.version_or_url(),
216            Self::Installable { dist, .. } => dist.version_or_url(),
217        }
218    }
219}
220
221impl Identifier for ResolvedDist {
222    fn distribution_id(&self) -> DistributionId {
223        match self {
224            Self::Installed { dist } => dist.distribution_id(),
225            Self::Installable { dist, .. } => dist.distribution_id(),
226        }
227    }
228
229    fn resource_id(&self) -> ResourceId {
230        match self {
231            Self::Installed { dist } => dist.resource_id(),
232            Self::Installable { dist, .. } => dist.resource_id(),
233        }
234    }
235}
236
237impl Display for ResolvedDist {
238    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
239        match self {
240            Self::Installed { dist } => dist.fmt(f),
241            Self::Installable { dist, .. } => dist.fmt(f),
242        }
243    }
244}