Skip to main content

uv_distribution_types/
buildable.rs

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