Skip to content

Commit

Permalink
Use a cross-platform representation for relative paths
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed May 23, 2024
1 parent 5bebadd commit 726c048
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 12 deletions.
8 changes: 8 additions & 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ rand = { version = "0.8.5" }
rayon = { version = "1.8.0" }
reflink-copy = { version = "0.1.15" }
regex = { version = "1.10.2" }
relative-path = { version = "1.9.3" }
reqwest = { version = "0.12.3", default-features = false, features = ["json", "gzip", "brotli", "stream", "rustls-tls", "rustls-tls-native-roots"] }
reqwest-middleware = { version = "0.3.0" }
reqwest-retry = { version = "0.5.0" }
Expand Down
2 changes: 2 additions & 0 deletions crates/uv-fs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ fs-err = { workspace = true }
fs2 = { workspace = true }
once_cell = { workspace = true }
path-absolutize = { workspace = true }
relative-path = { workspace = true }
itertools = { workspace = true }
tempfile = { workspace = true }
tracing = { workspace = true }
urlencoding = { workspace = true }
Expand Down
41 changes: 36 additions & 5 deletions crates/uv-fs/src/path.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use itertools::Either;
use std::borrow::Cow;
use std::path::{Component, Path, PathBuf};

use once_cell::sync::Lazy;
use relative_path::RelativePath;

/// The current working directory.
pub static CWD: Lazy<PathBuf> =
Expand Down Expand Up @@ -33,7 +35,13 @@ pub trait Simplified {
/// Render a [`Path`] for user-facing display.
///
/// Like [`simplified_display`], but relativizes the path against the current working directory.
fn user_display(&self) -> std::path::Display;
fn user_display(&self) -> impl std::fmt::Display;

/// Render a [`Path`] for user-facing display, where the [`Path`] is relative to a base path.
///
/// If the [`Path`] is not relative to the base path, will attempt to relativize the path
/// against the current working directory.
fn relative_display(&self, base: impl AsRef<Path>) -> impl std::fmt::Display;
}

impl<T: AsRef<Path>> Simplified for T {
Expand All @@ -49,17 +57,40 @@ impl<T: AsRef<Path>> Simplified for T {
dunce::canonicalize(self.as_ref())
}

fn user_display(&self) -> std::path::Display {
fn user_display(&self) -> impl std::fmt::Display {
let path = dunce::simplified(self.as_ref());

// Attempt to strip the current working directory, then the canonicalized current working
// directory, in case they differ.
path.strip_prefix(CWD.simplified())
.unwrap_or_else(|_| {
let path = path.strip_prefix(CWD.simplified()).unwrap_or_else(|_| {
path.strip_prefix(CANONICAL_CWD.simplified())
.unwrap_or(path)
});

// Use a portable representation for relative paths.
match RelativePath::from_path(path) {
Ok(path) => Either::Left(path),
Err(_) => Either::Right(path.display()),
}
}

fn relative_display(&self, base: impl AsRef<Path>) -> impl std::fmt::Display {
let path = dunce::simplified(self.as_ref());

// Attempt to strip the base, then the current working directory, then the canonicalized
// current working directory, in case they differ.
let path = path.strip_prefix(base.as_ref()).unwrap_or_else(|_| {
path.strip_prefix(CWD.simplified()).unwrap_or_else(|_| {
path.strip_prefix(CANONICAL_CWD.simplified())
.unwrap_or(path)
})
.display()
});

// Use a portable representation for relative paths.
match RelativePath::from_path(path) {
Ok(path) => Either::Left(path),
Err(_) => Either::Right(path.display()),
}
}
}

Expand Down
8 changes: 1 addition & 7 deletions crates/uv-interpreter/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1256,17 +1256,11 @@ impl fmt::Display for InterpreterNotFound {
path.user_display()
),
Self::ExecutableNotFoundInDirectory(directory, executable) => {
let executable = if let Ok(relative_executable) = executable.strip_prefix(directory)
{
relative_executable.display()
} else {
executable.user_display()
};
write!(
f,
"Interpreter directory `{}` does not contain Python executable at `{}`",
directory.user_display(),
executable
executable.relative_display(directory)
)
}
Self::ExecutableNotFoundInSearchPath(name) => {
Expand Down

0 comments on commit 726c048

Please sign in to comment.