Skip to content

Commit

Permalink
feat: Implement tilde expansion for path env
Browse files Browse the repository at this point in the history
  • Loading branch information
Xaeroxe committed Jun 11, 2023
1 parent e5ec711 commit dd2b7bb
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 2 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ categories = ["os", "filesystem"]
keywords = ["which", "which-rs", "unix", "command"]

[dependencies]
dirs = "5.0.1"
either = "1.6.1"
libc = "0.2.121"
regex = { version = "1.5.5", optional = true }
Expand Down
23 changes: 21 additions & 2 deletions src/finder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ use crate::checker::CompositeChecker;
use crate::error::*;
#[cfg(windows)]
use crate::helper::has_executable_extension;
use dirs::home_dir;
use either::Either;
#[cfg(feature = "regex")]
use regex::Regex;
#[cfg(feature = "regex")]
use std::borrow::Borrow;
use std::borrow::Cow;
use std::env;
use std::ffi::OsStr;
#[cfg(any(feature = "regex", target_os = "windows"))]
use std::fs;
use std::iter;
use std::path::{Path, PathBuf};
use std::path::{Component, Path, PathBuf};

pub trait Checker {
fn is_valid(&self, path: &Path) -> bool;
Expand Down Expand Up @@ -135,7 +137,9 @@ impl Finder {
where
P: IntoIterator<Item = PathBuf>,
{
let new_paths = paths.into_iter().map(move |p| p.join(binary_name.clone()));
let new_paths = paths
.into_iter()
.map(move |p| tilde_expansion(&p).join(binary_name.clone()));

Self::append_extension(new_paths)
}
Expand Down Expand Up @@ -210,6 +214,21 @@ impl Finder {
}
}

fn tilde_expansion(p: &PathBuf) -> Cow<'_, PathBuf> {
let mut component_iter = p.components();
if let Some(Component::Normal(o)) = component_iter.next() {
if o == "~" {
let mut new_path = home_dir().unwrap_or_default();
new_path.extend(component_iter);
Cow::Owned(new_path)
} else {
Cow::Borrowed(p)
}
} else {
Cow::Borrowed(p)
}
}

#[cfg(target_os = "windows")]
fn correct_casing(mut p: PathBuf) -> PathBuf {
if let (Some(parent), Some(file_name)) = (p.parent(), p.file_name()) {
Expand Down
47 changes: 47 additions & 0 deletions tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::path::{Path, PathBuf};
use std::{env, vec};
use tempfile::TempDir;

#[derive(Debug)]
struct TestFixture {
/// Temp directory.
pub tempdir: TempDir,
Expand Down Expand Up @@ -84,6 +85,32 @@ impl TestFixture {
}
}

pub fn new_with_tilde_path() -> TestFixture {
let tempdir = tempfile::tempdir().unwrap();
let mut builder = fs::DirBuilder::new();
builder.recursive(true);
let mut paths = vec![];
let mut bins = vec![];
for d in SUBDIRS.iter() {
let p = PathBuf::from("~").join(d);
let p_bin = tempdir.path().join(d);
builder.create(&p_bin).unwrap();
bins.push(mk_bin(&p_bin, BIN_NAME, "").unwrap());
bins.push(mk_bin(&p_bin, BIN_NAME, "exe").unwrap());
bins.push(mk_bin(&p_bin, BIN_NAME, "cmd").unwrap());
paths.push(p);
}
let p = tempdir.path().join("win-bin");
builder.create(&p).unwrap();
bins.push(mk_bin(&p, "win-bin", "exe").unwrap());
paths.push(p);
TestFixture {
tempdir,
paths: env::join_paths(paths).unwrap(),
bins,
}
}

#[allow(dead_code)]
pub fn touch(&self, path: &str, extension: &str) -> io::Result<PathBuf> {
touch(self.tempdir.path(), path, extension)
Expand Down Expand Up @@ -136,6 +163,26 @@ fn test_which() {
assert_eq!(_which(&f, BIN_NAME).unwrap(), f.bins[1])
}

#[test]
#[cfg(unix)]
fn test_which_tilde() {
let old_home = env::var_os("HOME");
let f = TestFixture::new_with_tilde_path();
env::set_var("HOME", f.tempdir.path().as_os_str());
assert_eq!(_which(&f, BIN_NAME).unwrap(), f.bins[0]);
if let Some(old_home) = old_home {
env::set_var("HOME", old_home);
} else {
env::remove_var("HOME");
}
}

// Windows test_which_tilde intentionally omitted because
// we don't want to pollute the home directory.
// It's non-trivial to adjust which directory Windows thinks
// is the home directory. At this time, tilde expansion has
// no Windows specific behavior. It works as normal on Windows.

#[test]
#[cfg(all(unix, feature = "regex"))]
fn test_which_re_in_with_matches() {
Expand Down

0 comments on commit dd2b7bb

Please sign in to comment.