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