Skip to main content

uv_distribution_types/
buildable.rs

1use std::borrow::Cow;
2use std::fmt;
3use std::path::Path;
4
5use owo_colors::OwoColorize;
6
7use uv_distribution_filename::SourceDistExtension;
8use uv_git_types::GitUrl;
9use uv_pep440::{Version, VersionSpecifiers};
10use uv_pep508::VerbatimUrl;
11
12use uv_normalize::PackageName;
13use uv_redacted::SafeUrl;
14
15use crate::{
16    DirectorySourceDist, DistributionMetadata, GitDirectorySourceDist, GitPathSourceDist, Name,
17    PathSourceDist, SourceDist,
18};
19
20/// A reference to a source that can be built into a built distribution.
21///
22/// This can either be a distribution (e.g., a package on a registry) or a direct URL.
23///
24/// Distributions can _also_ point to URLs in lieu of a registry; however, the primary distinction
25/// here is that a distribution will always include a package name, while a URL will not.
26#[derive(Debug, Clone)]
27pub enum BuildableSource<'a> {
28    Dist(&'a SourceDist),
29    Url(SourceUrl<'a>),
30}
31
32impl BuildableSource<'_> {
33    /// Display the source with its distribution's version or URL dimmed.
34    pub fn color_display(&self) -> impl fmt::Display + '_ {
35        fmt::from_fn(move |formatter| match self {
36            Self::Dist(dist) => write!(
37                formatter,
38                "{}{}",
39                dist.name(),
40                dist.version_or_url().dimmed()
41            ),
42            Self::Url(url) => fmt::Display::fmt(url, formatter),
43        })
44    }
45
46    /// Return the [`PackageName`] of the source, if available.
47    pub fn name(&self) -> Option<&PackageName> {
48        match self {
49            Self::Dist(dist) => Some(dist.name()),
50            Self::Url(_) => None,
51        }
52    }
53
54    /// Return the source tree of the source, if available.
55    pub fn source_tree(&self) -> Option<&Path> {
56        match self {
57            Self::Dist(dist) => dist.source_tree(),
58            Self::Url(url) => url.source_tree(),
59        }
60    }
61
62    /// Return the [`Version`] of the source, if available.
63    pub fn version(&self) -> Option<&Version> {
64        match self {
65            Self::Dist(SourceDist::Registry(dist)) => Some(&dist.version),
66            Self::Dist(SourceDist::Path(dist)) => dist.version.as_ref(),
67            Self::Dist(_) => None,
68            Self::Url(_) => None,
69        }
70    }
71
72    /// Return the [`BuildableSource`] as a [`SourceDist`], if it is a distribution.
73    pub fn as_dist(&self) -> Option<&SourceDist> {
74        match self {
75            Self::Dist(dist) => Some(dist),
76            Self::Url(_) => None,
77        }
78    }
79
80    /// Returns `true` if the source is editable.
81    pub fn is_editable(&self) -> bool {
82        match self {
83            Self::Dist(dist) => dist.is_editable(),
84            Self::Url(url) => url.is_editable(),
85        }
86    }
87
88    /// Returns `true` if the source is a first-party workspace member.
89    pub fn is_first_party(&self) -> bool {
90        match self {
91            Self::Dist(dist) => dist.is_first_party(),
92            Self::Url(_) => false,
93        }
94    }
95
96    /// Return true if the source refers to a local source tree (i.e., a directory).
97    pub fn is_source_tree(&self) -> bool {
98        match self {
99            Self::Dist(dist) => matches!(dist, SourceDist::Directory(_)),
100            Self::Url(url) => matches!(url, SourceUrl::Directory(_)),
101        }
102    }
103
104    /// Return the Python version specifier required by the source, if available.
105    pub fn requires_python(&self) -> Option<&VersionSpecifiers> {
106        let Self::Dist(SourceDist::Registry(dist)) = self else {
107            return None;
108        };
109        dist.file.requires_python.as_deref()
110    }
111}
112
113impl std::fmt::Display for BuildableSource<'_> {
114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        match self {
116            Self::Dist(dist) => write!(f, "{dist}"),
117            Self::Url(url) => write!(f, "{url}"),
118        }
119    }
120}
121
122/// A reference to a source distribution defined by a URL.
123#[derive(Debug, Clone)]
124pub enum SourceUrl<'a> {
125    Direct(DirectSourceUrl<'a>),
126    GitDirectory(GitDirectorySourceUrl<'a>),
127    GitPath(GitPathSourceUrl<'a>),
128    Path(PathSourceUrl<'a>),
129    Directory(DirectorySourceUrl<'a>),
130}
131
132impl SourceUrl<'_> {
133    /// Return the [`SafeUrl`] of the source.
134    pub fn url(&self) -> &SafeUrl {
135        match self {
136            Self::Direct(dist) => dist.url,
137            Self::GitDirectory(dist) => dist.url,
138            Self::GitPath(dist) => dist.url,
139            Self::Path(dist) => dist.url,
140            Self::Directory(dist) => dist.url,
141        }
142    }
143
144    /// Return the source tree of the source, if available.
145    fn source_tree(&self) -> Option<&Path> {
146        match self {
147            Self::Directory(dist) => Some(dist.install_path),
148            _ => None,
149        }
150    }
151
152    /// Returns `true` if the source is editable.
153    fn is_editable(&self) -> bool {
154        matches!(
155            self,
156            Self::Directory(DirectorySourceUrl {
157                editable: Some(true),
158                ..
159            })
160        )
161    }
162}
163
164impl std::fmt::Display for SourceUrl<'_> {
165    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166        match self {
167            Self::Direct(url) => write!(f, "{url}"),
168            Self::GitDirectory(url) => write!(f, "{url}"),
169            Self::GitPath(url) => write!(f, "{url}"),
170            Self::Path(url) => write!(f, "{url}"),
171            Self::Directory(url) => write!(f, "{url}"),
172        }
173    }
174}
175
176#[derive(Debug, Clone)]
177pub struct DirectSourceUrl<'a> {
178    pub url: &'a SafeUrl,
179    pub subdirectory: Option<&'a Path>,
180    pub ext: SourceDistExtension,
181}
182
183impl std::fmt::Display for DirectSourceUrl<'_> {
184    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185        write!(f, "{url}", url = self.url)
186    }
187}
188
189#[derive(Debug, Clone)]
190pub struct GitPathSourceUrl<'a> {
191    /// The URL with the revision and path fragment.
192    pub url: &'a VerbatimUrl,
193    pub git: &'a GitUrl,
194    pub path: Cow<'a, Path>,
195    pub ext: SourceDistExtension,
196}
197
198impl std::fmt::Display for GitPathSourceUrl<'_> {
199    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200        write!(f, "{url}", url = self.url)
201    }
202}
203
204impl<'a> From<&'a GitPathSourceDist> for GitPathSourceUrl<'a> {
205    fn from(dist: &'a GitPathSourceDist) -> Self {
206        Self {
207            url: &dist.url,
208            git: &dist.git,
209            path: Cow::Borrowed(&dist.install_path),
210            ext: dist.ext,
211        }
212    }
213}
214
215#[derive(Debug, Clone)]
216pub struct GitDirectorySourceUrl<'a> {
217    /// The URL with the revision and subdirectory fragment.
218    pub url: &'a VerbatimUrl,
219    pub git: &'a GitUrl,
220    /// The URL without the revision and subdirectory fragment.
221    pub subdirectory: Option<&'a Path>,
222}
223
224impl std::fmt::Display for GitDirectorySourceUrl<'_> {
225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226        write!(f, "{url}", url = self.url)
227    }
228}
229
230impl<'a> From<&'a GitDirectorySourceDist> for GitDirectorySourceUrl<'a> {
231    fn from(dist: &'a GitDirectorySourceDist) -> Self {
232        Self {
233            url: &dist.url,
234            git: &dist.git,
235            subdirectory: dist.subdirectory.as_deref(),
236        }
237    }
238}
239
240#[derive(Debug, Clone)]
241pub struct PathSourceUrl<'a> {
242    pub url: &'a SafeUrl,
243    pub path: Cow<'a, Path>,
244    pub ext: SourceDistExtension,
245}
246
247impl std::fmt::Display for PathSourceUrl<'_> {
248    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249        write!(f, "{url}", url = self.url)
250    }
251}
252
253impl<'a> From<&'a PathSourceDist> for PathSourceUrl<'a> {
254    fn from(dist: &'a PathSourceDist) -> Self {
255        Self {
256            url: &dist.url,
257            path: Cow::Borrowed(&dist.install_path),
258            ext: dist.ext,
259        }
260    }
261}
262
263#[derive(Debug, Clone)]
264pub struct DirectorySourceUrl<'a> {
265    pub url: &'a SafeUrl,
266    pub install_path: &'a Path,
267    pub editable: Option<bool>,
268}
269
270impl std::fmt::Display for DirectorySourceUrl<'_> {
271    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272        write!(f, "{url}", url = self.url)
273    }
274}
275
276impl<'a> From<&'a DirectorySourceDist> for DirectorySourceUrl<'a> {
277    fn from(dist: &'a DirectorySourceDist) -> Self {
278        Self {
279            url: &dist.url,
280            install_path: &dist.install_path,
281            editable: dist.editable,
282        }
283    }
284}