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#[derive(Debug, Clone)]
27pub enum BuildableSource<'a> {
28 Dist(&'a SourceDist),
29 Url(SourceUrl<'a>),
30}
31
32impl BuildableSource<'_> {
33 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 pub fn name(&self) -> Option<&PackageName> {
48 match self {
49 Self::Dist(dist) => Some(dist.name()),
50 Self::Url(_) => None,
51 }
52 }
53
54 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 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 pub fn as_dist(&self) -> Option<&SourceDist> {
74 match self {
75 Self::Dist(dist) => Some(dist),
76 Self::Url(_) => None,
77 }
78 }
79
80 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 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 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 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#[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 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 fn source_tree(&self) -> Option<&Path> {
146 match self {
147 Self::Directory(dist) => Some(dist.install_path),
148 _ => None,
149 }
150 }
151
152 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 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 pub url: &'a VerbatimUrl,
219 pub git: &'a GitUrl,
220 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}