From 8f2f43c5614e0e0723afdd039a819d05ed880fc3 Mon Sep 17 00:00:00 2001 From: konsti Date: Fri, 18 Jul 2025 14:08:49 +0200 Subject: [PATCH] Add a reusable path-or-URL parser (#14712) Reviewing #14687, I noticed that we had implemented a `Url::from_url_or_path`-like function, but it wasn't reusable. This change `Verbatim::from_url_or_path` so we can use it in other places too. The PEP 508 parser is an odd place for this, but that's where `VerbatimUrl` and `Scheme` are already living. --- crates/uv-distribution-types/src/index_url.rs | 29 +----------- crates/uv-pep508/src/verbatim_url.rs | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/crates/uv-distribution-types/src/index_url.rs b/crates/uv-distribution-types/src/index_url.rs index bd3e9abc2..cbc1a4eb1 100644 --- a/crates/uv-distribution-types/src/index_url.rs +++ b/crates/uv-distribution-types/src/index_url.rs @@ -39,33 +39,8 @@ impl IndexUrl { /// If no root directory is provided, relative paths are resolved against the current working /// directory. pub fn parse(path: &str, root_dir: Option<&Path>) -> Result { - let url = match split_scheme(path) { - Some((scheme, ..)) => { - match Scheme::parse(scheme) { - Some(_) => { - // Ex) `https://pypi.org/simple` - VerbatimUrl::parse_url(path)? - } - None => { - // Ex) `C:\Users\user\index` - if let Some(root_dir) = root_dir { - VerbatimUrl::from_path(path, root_dir)? - } else { - VerbatimUrl::from_absolute_path(std::path::absolute(path)?)? - } - } - } - } - None => { - // Ex) `/Users/user/index` - if let Some(root_dir) = root_dir { - VerbatimUrl::from_path(path, root_dir)? - } else { - VerbatimUrl::from_absolute_path(std::path::absolute(path)?)? - } - } - }; - Ok(Self::from(url.with_given(path))) + let url = VerbatimUrl::from_url_or_path(path, root_dir)?; + Ok(Self::from(url)) } /// Return the root [`Url`] of the index, if applicable. diff --git a/crates/uv-pep508/src/verbatim_url.rs b/crates/uv-pep508/src/verbatim_url.rs index 37d07b40b..2911de938 100644 --- a/crates/uv-pep508/src/verbatim_url.rs +++ b/crates/uv-pep508/src/verbatim_url.rs @@ -58,6 +58,48 @@ impl VerbatimUrl { }) } + /// Convert a [`VerbatimUrl`] from a path or a URL. + /// + /// If no root directory is provided, relative paths are resolved against the current working + /// directory. + pub fn from_url_or_path( + input: &str, + root_dir: Option<&Path>, + ) -> Result { + let url = match split_scheme(input) { + Some((scheme, ..)) => { + match Scheme::parse(scheme) { + Some(_) => { + // Ex) `https://pypi.org/simple` + Self::parse_url(input)? + } + None => { + // Ex) `C:\Users\user\index` + if let Some(root_dir) = root_dir { + Self::from_path(input, root_dir)? + } else { + let absolute_path = std::path::absolute(input).map_err(|err| { + VerbatimUrlError::Absolute(input.to_string(), err) + })?; + Self::from_absolute_path(absolute_path)? + } + } + } + } + None => { + // Ex) `/Users/user/index` + if let Some(root_dir) = root_dir { + Self::from_path(input, root_dir)? + } else { + let absolute_path = std::path::absolute(input) + .map_err(|err| VerbatimUrlError::Absolute(input.to_string(), err))?; + Self::from_absolute_path(absolute_path)? + } + } + }; + Ok(url.with_given(input)) + } + /// Parse a URL from an absolute or relative path. #[cfg(feature = "non-pep508-extensions")] // PEP 508 arguably only allows absolute file URLs. pub fn from_path( @@ -362,6 +404,10 @@ pub enum VerbatimUrlError { #[error("path could not be normalized: {0}")] Normalization(PathBuf, #[source] std::io::Error), + /// Received a path that could not be converted to an absolute path. + #[error("path could not be converted to an absolute path: {0}")] + Absolute(String, #[source] std::io::Error), + /// Received a path that could not be normalized. #[cfg(not(feature = "non-pep508-extensions"))] #[error("Not a URL (missing scheme): {0}")]