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    pub 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    pub fn is_editable(&self) -> bool {
130        matches!(
131            self,
132            Self::Directory(DirectorySourceUrl {
133                editable: Some(true),
134                ..
135            })
136        )
137    }
138
139    /// Return true if the source refers to a local file or directory.
140    pub fn is_local(&self) -> bool {
141        matches!(self, Self::Path(_) | Self::Directory(_))
142    }
143}
144
145impl std::fmt::Display for SourceUrl<'_> {
146    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147        match self {
148            Self::Direct(url) => write!(f, "{url}"),
149            Self::GitDirectory(url) => write!(f, "{url}"),
150            Self::GitPath(url) => write!(f, "{url}"),
151            Self::Path(url) => write!(f, "{url}"),
152            Self::Directory(url) => write!(f, "{url}"),
153        }
154    }
155}
156
157#[derive(Debug, Clone)]
158pub struct DirectSourceUrl<'a> {
159    pub url: &'a DisplaySafeUrl,
160    pub subdirectory: Option<&'a Path>,
161    pub ext: SourceDistExtension,
162}
163
164impl std::fmt::Display for DirectSourceUrl<'_> {
165    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166        write!(f, "{url}", url = self.url)
167    }
168}
169
170#[derive(Debug, Clone)]
171pub struct GitPathSourceUrl<'a> {
172    /// The URL with the revision and path fragment.
173    pub url: &'a VerbatimUrl,
174    pub git: &'a GitUrl,
175    pub path: Cow<'a, Path>,
176    pub ext: SourceDistExtension,
177}
178
179impl std::fmt::Display for GitPathSourceUrl<'_> {
180    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181        write!(f, "{url}", url = self.url)
182    }
183}
184
185impl<'a> From<&'a GitPathSourceDist> for GitPathSourceUrl<'a> {
186    fn from(dist: &'a GitPathSourceDist) -> Self {
187        Self {
188            url: &dist.url,
189            git: &dist.git,
190            path: Cow::Borrowed(&dist.install_path),
191            ext: dist.ext,
192        }
193    }
194}
195
196#[derive(Debug, Clone)]
197pub struct GitDirectorySourceUrl<'a> {
198    /// The URL with the revision and subdirectory fragment.
199    pub url: &'a VerbatimUrl,
200    pub git: &'a GitUrl,
201    /// The URL without the revision and subdirectory fragment.
202    pub subdirectory: Option<&'a Path>,
203}
204
205impl std::fmt::Display for GitDirectorySourceUrl<'_> {
206    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207        write!(f, "{url}", url = self.url)
208    }
209}
210
211impl<'a> From<&'a GitDirectorySourceDist> for GitDirectorySourceUrl<'a> {
212    fn from(dist: &'a GitDirectorySourceDist) -> Self {
213        Self {
214            url: &dist.url,
215            git: &dist.git,
216            subdirectory: dist.subdirectory.as_deref(),
217        }
218    }
219}
220
221#[derive(Debug, Clone)]
222pub struct PathSourceUrl<'a> {
223    pub url: &'a DisplaySafeUrl,
224    pub path: Cow<'a, Path>,
225    pub ext: SourceDistExtension,
226}
227
228impl std::fmt::Display for PathSourceUrl<'_> {
229    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230        write!(f, "{url}", url = self.url)
231    }
232}
233
234impl<'a> From<&'a PathSourceDist> for PathSourceUrl<'a> {
235    fn from(dist: &'a PathSourceDist) -> Self {
236        Self {
237            url: &dist.url,
238            path: Cow::Borrowed(&dist.install_path),
239            ext: dist.ext,
240        }
241    }
242}
243
244#[derive(Debug, Clone)]
245pub struct DirectorySourceUrl<'a> {
246    pub url: &'a DisplaySafeUrl,
247    pub install_path: &'a Path,
248    pub editable: Option<bool>,
249}
250
251impl std::fmt::Display for DirectorySourceUrl<'_> {
252    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253        write!(f, "{url}", url = self.url)
254    }
255}
256
257impl<'a> From<&'a DirectorySourceDist> for DirectorySourceUrl<'a> {
258    fn from(dist: &'a DirectorySourceDist) -> Self {
259        Self {
260            url: &dist.url,
261            install_path: &dist.install_path,
262            editable: dist.editable,
263        }
264    }
265}