diff --git a/crates/distribution-types/src/index_url.rs b/crates/distribution-types/src/index_url.rs index 0b03fd1b2955b..029cb547af73d 100644 --- a/crates/distribution-types/src/index_url.rs +++ b/crates/distribution-types/src/index_url.rs @@ -1,14 +1,15 @@ use std::borrow::Cow; use std::fmt::{Display, Formatter}; use std::ops::Deref; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::str::FromStr; use itertools::Either; use once_cell::sync::Lazy; -use url::Url; +use thiserror::Error; +use url::{ParseError, Url}; -use pep508_rs::{expand_env_vars, split_scheme, strip_host, Scheme, VerbatimUrl}; +use pep508_rs::{expand_env_vars, split_scheme, strip_host, Scheme, VerbatimUrl, VerbatimUrlError}; use uv_fs::normalize_url_path; use crate::Verbatim; @@ -90,18 +91,44 @@ impl Verbatim for IndexUrl { } } +/// An error that can occur when parsing an [`IndexUrl`]. +#[derive(Error, Debug)] +pub enum IndexUrlError { + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Url(#[from] ParseError), + #[error(transparent)] + VerbatimUrl(#[from] VerbatimUrlError), + #[error("Index URL must be a valid base URL")] + CannotBeABase, +} + impl FromStr for IndexUrl { - type Err = url::ParseError; + type Err = IndexUrlError; fn from_str(s: &str) -> Result { - let url = Url::parse(s)?; - let url = VerbatimUrl::from_url(url).with_given(s.to_owned()); - if *url.raw() == *PYPI_URL { - Ok(Self::Pypi(url)) - } else if url.scheme() == "file" { - Ok(Self::Path(url)) - } else { - Ok(Self::Url(url)) + match Path::new(s).canonicalize() { + Ok(path) => { + let url = VerbatimUrl::from_path(path)?.with_given(s.to_owned()); + Ok(Self::Path(url)) + } + Err(err) if err.kind() == std::io::ErrorKind::NotFound => { + let url = Url::parse(s)?; + if url.cannot_be_a_base() { + Err(IndexUrlError::CannotBeABase) + } else { + let url = VerbatimUrl::from_url(url).with_given(s.to_owned()); + if *url.raw() == *PYPI_URL { + Ok(Self::Pypi(url)) + } else if url.scheme() == "file" { + Ok(Self::Path(url)) + } else { + Ok(Self::Url(url)) + } + } + } + Err(err) => Err(err.into()), } } }