Skip to content

Commit

Permalink
Dedicated error type for direct url parsing (astral-sh#3181)
Browse files Browse the repository at this point in the history
Add a dedicated error type for direct url parsing. This change is broken
out from the new uv requirement type, which uses direct url parsing
internally.
  • Loading branch information
konstin authored Apr 22, 2024
1 parent 0e54cfb commit f29c991
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 17 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/distribution-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ uv-normalize = { workspace = true }

anyhow = { workspace = true }
fs-err = { workspace = true }
git2 = { workspace = true }
itertools = { workspace = true }
once_cell = { workspace = true }
rkyv = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/distribution-types/src/cached.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ impl CachedDist {
editable: dist.editable,
})))
} else {
DirectUrl::try_from(dist.url.raw()).map(Some)
Ok(Some(DirectUrl::try_from(dist.url.raw())?))
}
}
}
Expand Down
42 changes: 29 additions & 13 deletions crates/distribution-types/src/direct_url.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
use anyhow::{Error, Result};
use std::path::PathBuf;

use anyhow::{Context, Error, Result};
use thiserror::Error;
use url::Url;

use uv_git::{GitSha, GitUrl};

#[derive(Debug, Error)]
pub enum DirectUrlError {
#[error("Unsupported URL prefix `{prefix}` in URL: `{url}`")]
UnsupportedUrlPrefix { prefix: String, url: Url },
#[error("Invalid path in file URL: `{0}`")]
InvalidFileUrl(Url),
#[error("Failed to parse Git reference from URL: `{0}`")]
GitShaParse(Url, #[source] git2::Error),
#[error("Not a valid URL: `{0}`")]
UrlParse(String, #[source] url::ParseError),
#[error("Missing `git+` prefix for Git URL: `{0}`")]
MissingUrlPrefix(Url),
}

#[derive(Debug)]
pub enum DirectUrl {
/// The direct URL is a path to a local directory or file.
Expand Down Expand Up @@ -49,17 +63,18 @@ pub struct DirectArchiveUrl {
}

impl TryFrom<&Url> for DirectGitUrl {
type Error = Error;
type Error = DirectUrlError;

fn try_from(url: &Url) -> Result<Self, Self::Error> {
let subdirectory = get_subdirectory(url);
fn try_from(url_in: &Url) -> Result<Self, Self::Error> {
let subdirectory = get_subdirectory(url_in);

let url = url
let url = url_in
.as_str()
.strip_prefix("git+")
.context("Missing git+ prefix for Git URL")?;
let url = Url::parse(url)?;
let url = GitUrl::try_from(url)?;
.ok_or_else(|| DirectUrlError::MissingUrlPrefix(url_in.clone()))?;
let url = Url::parse(url).map_err(|err| DirectUrlError::UrlParse(url.to_string(), err))?;
let url = GitUrl::try_from(url)
.map_err(|err| DirectUrlError::GitShaParse(url_in.clone(), err))?;
Ok(Self { url, subdirectory })
}
}
Expand Down Expand Up @@ -94,15 +109,16 @@ pub fn git_reference(url: &Url) -> Result<Option<GitSha>, Error> {
}

impl TryFrom<&Url> for DirectUrl {
type Error = Error;
type Error = DirectUrlError;

fn try_from(url: &Url) -> Result<Self, Self::Error> {
if let Some((prefix, ..)) = url.scheme().split_once('+') {
match prefix {
"git" => Ok(Self::Git(DirectGitUrl::try_from(url)?)),
_ => Err(Error::msg(format!(
"Unsupported URL prefix `{prefix}` in URL: {url}",
))),
_ => Err(DirectUrlError::UnsupportedUrlPrefix {
prefix: prefix.to_string(),
url: url.clone(),
}),
}
} else if url.scheme().eq_ignore_ascii_case("file") {
Ok(Self::LocalFile(LocalFileUrl {
Expand Down
3 changes: 3 additions & 0 deletions crates/uv-distribution/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use tokio::task::JoinError;
use zip::result::ZipError;

use distribution_filename::WheelFilenameError;
use distribution_types::DirectUrlError;
use pep440_rs::Version;
use pypi_types::HashDigest;
use uv_client::BetterReqwestError;
Expand All @@ -23,6 +24,8 @@ pub enum Error {
#[error("Git operation failed")]
Git(#[source] anyhow::Error),
#[error(transparent)]
DirectUrl(#[from] Box<DirectUrlError>),
#[error(transparent)]
Reqwest(#[from] BetterReqwestError),
#[error(transparent)]
Client(#[from] uv_client::Error),
Expand Down
4 changes: 2 additions & 2 deletions crates/uv-distribution/src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub(crate) async fn fetch_git_archive(
)
.map_err(Error::CacheWrite)?;

let DirectGitUrl { url, subdirectory } = DirectGitUrl::try_from(url).map_err(Error::Git)?;
let DirectGitUrl { url, subdirectory } = DirectGitUrl::try_from(url).map_err(Box::new)?;

// Fetch the Git repository.
let source = if let Some(reporter) = reporter {
Expand Down Expand Up @@ -95,7 +95,7 @@ pub(crate) async fn resolve_precise(
cache: &Cache,
reporter: Option<&Arc<dyn Reporter>>,
) -> Result<Option<Url>, Error> {
let DirectGitUrl { url, subdirectory } = DirectGitUrl::try_from(url).map_err(Error::Git)?;
let DirectGitUrl { url, subdirectory } = DirectGitUrl::try_from(url).map_err(Box::new)?;

// If the Git reference already contains a complete SHA, short-circuit.
if url.precise().is_some() {
Expand Down
2 changes: 1 addition & 1 deletion crates/uv-git/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl GitUrl {
}

impl TryFrom<Url> for GitUrl {
type Error = anyhow::Error;
type Error = git2::Error;

/// Initialize a [`GitUrl`] source from a URL.
fn try_from(mut url: Url) -> Result<Self, Self::Error> {
Expand Down

0 comments on commit f29c991

Please sign in to comment.