diff --git a/Cargo.lock b/Cargo.lock index 9b8584c04..008b8a060 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4054,6 +4054,12 @@ dependencies = [ "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "usvg" version = "0.29.0" @@ -4409,6 +4415,7 @@ dependencies = [ "junction", "tempfile", "tracing", + "urlencoding", "uv-warnings", ] diff --git a/Cargo.toml b/Cargo.toml index 8579673c5..a3ad53a02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,6 +105,7 @@ tracing-tree = { version = "0.3.0" } unicode-width = { version = "0.1.11" } unscanny = { version = "0.1.0" } url = { version = "2.5.0" } +urlencoding = { version = "2.1.3" } uuid = { version = "1.7.0", default-features = false } walkdir = { version = "2.4.0" } which = { version = "6.0.0" } diff --git a/crates/uv-fs/Cargo.toml b/crates/uv-fs/Cargo.toml index af3cf42ec..5af8bd625 100644 --- a/crates/uv-fs/Cargo.toml +++ b/crates/uv-fs/Cargo.toml @@ -21,6 +21,7 @@ fs2 = { workspace = true } junction = { workspace = true } tempfile = { workspace = true } tracing = { workspace = true } +urlencoding = { workspace = true } [features] default = [] diff --git a/crates/uv-fs/src/path.rs b/crates/uv-fs/src/path.rs index 696055fc8..14ef90b0a 100644 --- a/crates/uv-fs/src/path.rs +++ b/crates/uv-fs/src/path.rs @@ -31,14 +31,18 @@ impl> Normalized for T { /// /// On other platforms, this is a no-op. pub fn normalize_url_path(path: &str) -> Cow<'_, str> { + // Apply percent-decoding to the URL. + let path = urlencoding::decode(path).unwrap_or(Cow::Borrowed(path)); + + // Return the path. if cfg!(windows) { Cow::Owned( path.strip_prefix('/') - .unwrap_or(path) + .unwrap_or(&path) .replace('/', std::path::MAIN_SEPARATOR_STR), ) } else { - Cow::Borrowed(path) + path } } @@ -71,5 +75,17 @@ mod tests { "./ferris/wheel-0.42.0.tar.gz" ); } + + if cfg!(windows) { + assert_eq!( + normalize_url_path("./wheel%20cache/wheel-0.42.0.tar.gz"), + ".\\wheel cache\\wheel-0.42.0.tar.gz" + ); + } else { + assert_eq!( + normalize_url_path("./wheel%20cache/wheel-0.42.0.tar.gz"), + "./wheel cache/wheel-0.42.0.tar.gz" + ); + } } }