|
6 | 6 | //!
|
7 | 7 | //! Then lowers them into a dependency specification.
|
8 | 8 |
|
9 |
| -use std::collections::BTreeMap; |
10 | 9 | use std::ops::Deref;
|
| 10 | +use std::{collections::BTreeMap, mem}; |
11 | 11 |
|
12 | 12 | use glob::Pattern;
|
13 | 13 | use serde::{Deserialize, Serialize};
|
| 14 | +use thiserror::Error; |
14 | 15 | use url::Url;
|
15 | 16 |
|
16 | 17 | use pep440_rs::VersionSpecifiers;
|
17 |
| -use pypi_types::VerbatimParsedUrl; |
| 18 | +use pypi_types::{RequirementSource, VerbatimParsedUrl}; |
| 19 | +use uv_git::GitReference; |
18 | 20 | use uv_normalize::{ExtraName, PackageName};
|
19 | 21 |
|
20 | 22 | /// A `pyproject.toml` as specified in PEP 517.
|
@@ -182,6 +184,90 @@ pub enum Source {
|
182 | 184 | },
|
183 | 185 | }
|
184 | 186 |
|
| 187 | +#[derive(Error, Debug)] |
| 188 | +pub enum SourceError { |
| 189 | + #[error("Cannot resolve git reference `{0}`.")] |
| 190 | + UnresolvedReference(String), |
| 191 | + #[error("Workspace dependency must be a local path.")] |
| 192 | + InvalidWorkspaceRequirement, |
| 193 | +} |
| 194 | + |
| 195 | +impl Source { |
| 196 | + pub fn from_requirement( |
| 197 | + source: RequirementSource, |
| 198 | + workspace: bool, |
| 199 | + editable: Option<bool>, |
| 200 | + rev: Option<String>, |
| 201 | + tag: Option<String>, |
| 202 | + branch: Option<String>, |
| 203 | + ) -> Result<Option<Source>, SourceError> { |
| 204 | + if workspace { |
| 205 | + match source { |
| 206 | + RequirementSource::Registry { .. } | RequirementSource::Directory { .. } => {} |
| 207 | + _ => return Err(SourceError::InvalidWorkspaceRequirement), |
| 208 | + } |
| 209 | + |
| 210 | + return Ok(Some(Source::Workspace { |
| 211 | + editable, |
| 212 | + workspace: true, |
| 213 | + })); |
| 214 | + } |
| 215 | + |
| 216 | + let source = match source { |
| 217 | + RequirementSource::Registry { .. } => return Ok(None), |
| 218 | + RequirementSource::Path { lock_path, .. } => Source::Path { |
| 219 | + editable, |
| 220 | + path: lock_path.to_string_lossy().into_owned(), |
| 221 | + }, |
| 222 | + RequirementSource::Directory { lock_path, .. } => Source::Path { |
| 223 | + editable, |
| 224 | + path: lock_path.to_string_lossy().into_owned(), |
| 225 | + }, |
| 226 | + RequirementSource::Url { |
| 227 | + subdirectory, url, .. |
| 228 | + } => Source::Url { |
| 229 | + url: url.to_url(), |
| 230 | + subdirectory: subdirectory.map(|path| path.to_string_lossy().into_owned()), |
| 231 | + }, |
| 232 | + RequirementSource::Git { |
| 233 | + repository, |
| 234 | + mut reference, |
| 235 | + subdirectory, |
| 236 | + .. |
| 237 | + } => { |
| 238 | + // We can only resolve a full commit hash from a pep508 URL, everything else is ambiguous. |
| 239 | + let rev = match reference { |
| 240 | + GitReference::FullCommit(ref mut rev) => Some(mem::take(rev)), |
| 241 | + _ => None, |
| 242 | + } |
| 243 | + // Give precedence to an explicit argument. |
| 244 | + .or(rev); |
| 245 | + |
| 246 | + // Error if the user tried to specify a reference but didn't disambiguate. |
| 247 | + if reference != GitReference::DefaultBranch |
| 248 | + && rev.is_none() |
| 249 | + && tag.is_none() |
| 250 | + && branch.is_none() |
| 251 | + { |
| 252 | + return Err(SourceError::UnresolvedReference( |
| 253 | + reference.as_str().unwrap().to_owned(), |
| 254 | + )); |
| 255 | + } |
| 256 | + |
| 257 | + Source::Git { |
| 258 | + rev, |
| 259 | + tag, |
| 260 | + branch, |
| 261 | + git: repository, |
| 262 | + subdirectory: subdirectory.map(|path| path.to_string_lossy().into_owned()), |
| 263 | + } |
| 264 | + } |
| 265 | + }; |
| 266 | + |
| 267 | + Ok(Some(source)) |
| 268 | + } |
| 269 | +} |
| 270 | + |
185 | 271 | /// <https://github.com/serde-rs/serde/issues/1316#issue-332908452>
|
186 | 272 | mod serde_from_and_to_string {
|
187 | 273 | use std::fmt::Display;
|
|
0 commit comments