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    /// Return true if the source refers to a local source tree (i.e., a directory).
73    pub fn is_source_tree(&self) -> bool {
74        match self {
75            Self::Dist(dist) => matches!(dist, SourceDist::Directory(_)),
76            Self::Url(url) => matches!(url, SourceUrl::Directory(_)),
77        }
78    }
79
80    /// Return the Python version specifier required by the source, if available.
81    pub fn requires_python(&self) -> Option<&VersionSpecifiers> {
82        let Self::Dist(SourceDist::Registry(dist)) = self else {
83            return None;
84        };
85        dist.file.requires_python.as_ref()
86    }
87}
88
89impl std::fmt::Display for BuildableSource<'_> {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        match self {
92            Self::Dist(dist) => write!(f, "{dist}"),
93            Self::Url(url) => write!(f, "{url}"),
94        }
95    }
96}
97
98/// A reference to a source distribution defined by a URL.
99#[derive(Debug, Clone)]
100pub enum SourceUrl<'a> {
101    Direct(DirectSourceUrl<'a>),
102    GitDirectory(GitDirectorySourceUrl<'a>),
103    GitPath(GitPathSourceUrl<'a>),
104    Path(PathSourceUrl<'a>),
105    Directory(DirectorySourceUrl<'a>),
106}
107
108impl SourceUrl<'_> {
109    /// Return the [`DisplaySafeUrl`] of the source.
110    pub fn url(&self) -> &DisplaySafeUrl {
111        match self {
112            Self::Direct(dist) => dist.url,
113            Self::GitDirectory(dist) => dist.url,
114            Self::GitPath(dist) => dist.url,
115            Self::Path(dist) => dist.url,
116            Self::Directory(dist) => dist.url,
117        }
118    }
119
120    /// Return the source tree of the source, if available.
121    fn source_tree(&self) -> Option<&Path> {
122        match self {
123            Self::Directory(dist) => Some(dist.install_path),
124            _ => None,
125        }
126    }
127
128    /// Returns `true` if the source is editable.
129    fn is_editable(&self) -> bool {
130        matches!(
131            self,
132            Self::Directory(DirectorySourceUrl {
133                editable: Some(true),
134                ..
135            })
136        )
137    }
138}
139
140impl std::fmt::Display for SourceUrl<'_> {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        match self {
143            Self::Direct(url) => write!(f, "{url}"),
144            Self::GitDirectory(url) => write!(f, "{url}"),
145            Self::GitPath(url) => write!(f, "{url}"),
146            Self::Path(url) => write!(f, "{url}"),
147            Self::Directory(url) => write!(f, "{url}"),
148        }
149    }
150}
151
152#[derive(Debug, Clone)]
153pub struct DirectSourceUrl<'a> {
154    pub url: &'a DisplaySafeUrl,
155    pub subdirectory: Option<&'a Path>,
156    pub ext: SourceDistExtension,
157}
158
159impl std::fmt::Display for DirectSourceUrl<'_> {
160    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161        write!(f, "{url}", url = self.url)
162    }
163}
164
165#[derive(Debug, Clone)]
166pub struct GitPathSourceUrl<'a> {
167    /// The URL with the revision and path fragment.
168    pub url: &'a VerbatimUrl,
169    pub git: &'a GitUrl,
170    pub path: Cow<'a, Path>,
171    pub ext: SourceDistExtension,
172}
173
174impl std::fmt::Display for GitPathSourceUrl<'_> {
175    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176        write!(f, "{url}", url = self.url)
177    }
178}
179
180impl<'a> From<&'a GitPathSourceDist> for GitPathSourceUrl<'a> {
181    fn from(dist: &'a GitPathSourceDist) -> Self {
182        Self {
183            url: &dist.url,
184            git: &dist.git,
185            path: Cow::Borrowed(&dist.install_path),
186            ext: dist.ext,
187        }
188    }
189}
190
191#[derive(Debug, Clone)]
192pub struct GitDirectorySourceUrl<'a> {
193    /// The URL with the revision and subdirectory fragment.
194    pub url: &'a VerbatimUrl,
195    pub git: &'a GitUrl,
196    /// The URL without the revision and subdirectory fragment.
197    pub subdirectory: Option<&'a Path>,
198}
199
200impl std::fmt::Display for GitDirectorySourceUrl<'_> {
201    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202        write!(f, "{url}", url = self.url)
203    }
204}
205
206impl<'a> From<&'a GitDirectorySourceDist> for GitDirectorySourceUrl<'a> {
207    fn from(dist: &'a GitDirectorySourceDist) -> Self {
208        Self {
209            url: &dist.url,
210            git: &dist.git,
211            subdirectory: dist.subdirectory.as_deref(),
212        }
213    }
214}
215
216#[derive(Debug, Clone)]
217pub struct PathSourceUrl<'a> {
218    pub url: &'a DisplaySafeUrl,
219    pub path: Cow<'a, Path>,
220    pub ext: SourceDistExtension,
221}
222
223impl std::fmt::Display for PathSourceUrl<'_> {
224    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225        write!(f, "{url}", url = self.url)
226    }
227}
228
229impl<'a> From<&'a PathSourceDist> for PathSourceUrl<'a> {
230    fn from(dist: &'a PathSourceDist) -> Self {
231        Self {
232            url: &dist.url,
233            path: Cow::Borrowed(&dist.install_path),
234            ext: dist.ext,
235        }
236    }
237}
238
239#[derive(Debug, Clone)]
240pub struct DirectorySourceUrl<'a> {
241    pub url: &'a DisplaySafeUrl,
242    pub install_path: &'a Path,
243    pub editable: Option<bool>,
244}
245
246impl std::fmt::Display for DirectorySourceUrl<'_> {
247    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248        write!(f, "{url}", url = self.url)
249    }
250}
251
252impl<'a> From<&'a DirectorySourceDist> for DirectorySourceUrl<'a> {
253    fn from(dist: &'a DirectorySourceDist) -> Self {
254        Self {
255            url: &dist.url,
256            install_path: &dist.install_path,
257            editable: dist.editable,
258        }
259    }
260}