aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Galenson <jgalenson@google.com>2021-08-13 03:12:43 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-08-13 03:12:43 +0000
commitff8245d7b3988f916e42a6d6ca3183b852e85f4a (patch)
treeee0741f958fe697978838edb95ab00e50af08beb
parentd1f21a518da6259761da0e31870c0910bfb767ee (diff)
parentc0a4e4addc7f57818a04cde4594a0b5867e6ceb2 (diff)
downloadwhich-ff8245d7b3988f916e42a6d6ca3183b852e85f4a.tar.gz
Upgrade rust/crates/which to 4.2.2 am: 9e4f7ef037 am: 86f4c737d3 am: ed6ff50032 am: c0a4e4addc
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/which/+/1790955 Change-Id: Iaec7eb862384db7e6fd9bf3fb2e5758061650a3a
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--.github/workflows/rust.yml19
-rw-r--r--.gitignore1
-rw-r--r--Android.bp2
-rw-r--r--Cargo.toml8
-rw-r--r--Cargo.toml.orig6
-rw-r--r--METADATA8
-rw-r--r--src/finder.rs79
-rw-r--r--src/helper.rs10
-rw-r--r--src/lib.rs64
-rw-r--r--tests/basic.rs36
11 files changed, 193 insertions, 42 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index fb1169d..26fe28d 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
{
"git": {
- "sha1": "2c54067bab846e96f07be60e78250f385825f09f"
+ "sha1": "bdd46e52931db0b693e30bc08c4edfa499392903"
}
}
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 288ccaf..5ac490b 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -1,16 +1,11 @@
name: Rust
-on:
- push:
- branches: [ master ]
- pull_request:
- branches: [ master ]
+on: [push, pull_request]
env:
CARGO_TERM_COLOR: always
jobs:
-
test:
name: Build and test
runs-on: ${{ matrix.os }}
@@ -18,11 +13,11 @@ jobs:
fail-fast: false
matrix:
os:
- - ubuntu-latest
- - windows-latest
- - macos-latest
+ - ubuntu-latest
+ - windows-latest
+ - macos-latest
steps:
- - uses: actions/checkout@v2
- - run: cargo build --verbose
- - run: cargo test --verbose
+ - uses: actions/checkout@v2
+ - run: cargo build --verbose
+ - run: cargo test --verbose
diff --git a/.gitignore b/.gitignore
index a9d37c5..865d4a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
target
Cargo.lock
+.vscode/
diff --git a/Android.bp b/Android.bp
index 141bf34..ef770b4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -32,4 +32,4 @@ rust_library {
// dependent_library ["feature_list"]
// either-1.6.1 "default,use_std"
-// libc-0.2.92 "default,std"
+// libc-0.2.98 "default,std"
diff --git a/Cargo.toml b/Cargo.toml
index f6e0a43..7814b7b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
[package]
edition = "2018"
name = "which"
-version = "4.1.0"
+version = "4.2.2"
authors = ["Harry Fei <tiziyuanfang@gmail.com>"]
description = "A Rust equivalent of Unix command \"which\". Locate installed executable in cross platforms."
documentation = "https://docs.rs/which/"
@@ -27,5 +27,11 @@ version = "1.6"
[dependencies.libc]
version = "0.2.65"
+
+[dependencies.regex]
+version = "1.5.4"
+optional = true
[dev-dependencies.tempdir]
version = "0.3.7"
+[target."cfg(windows)".dependencies.lazy_static]
+version = "1"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index c3c472e..12b29d3 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "which"
-version = "4.1.0"
+version = "4.2.2"
edition = "2018"
authors = ["Harry Fei <tiziyuanfang@gmail.com>"]
repository = "https://github.com/harryfei/which-rs.git"
@@ -14,6 +14,10 @@ keywords = ["which", "which-rs", "unix", "command"]
[dependencies]
either = "1.6"
libc = "0.2.65"
+regex = { version = "1.5.4", optional = true }
+
+[target.'cfg(windows)'.dependencies]
+lazy_static = "1"
[dev-dependencies]
tempdir = "0.3.7"
diff --git a/METADATA b/METADATA
index 27de0e1..ea19f37 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/which/which-4.1.0.crate"
+ value: "https://static.crates.io/crates/which/which-4.2.2.crate"
}
- version: "4.1.0"
+ version: "4.2.2"
license_type: NOTICE
last_upgrade_date {
year: 2021
- month: 4
- day: 2
+ month: 8
+ day: 9
}
}
diff --git a/src/finder.rs b/src/finder.rs
index 91c6cab..416ee3c 100644
--- a/src/finder.rs
+++ b/src/finder.rs
@@ -1,12 +1,14 @@
use crate::checker::CompositeChecker;
use crate::error::*;
-use either::Either;
#[cfg(windows)]
use crate::helper::has_executable_extension;
+use either::Either;
+#[cfg(feature = "regex")]
+use regex::Regex;
use std::env;
use std::ffi::OsStr;
-#[cfg(windows)]
-use std::ffi::OsString;
+#[cfg(feature = "regex")]
+use std::fs;
use std::iter;
use std::path::{Path, PathBuf};
@@ -76,6 +78,37 @@ impl Finder {
Ok(binary_path_candidates.filter(move |p| binary_checker.is_valid(p)))
}
+ #[cfg(feature = "regex")]
+ pub fn find_re<T>(
+ &self,
+ binary_regex: Regex,
+ paths: Option<T>,
+ binary_checker: CompositeChecker,
+ ) -> Result<impl Iterator<Item = PathBuf>>
+ where
+ T: AsRef<OsStr>,
+ {
+ let p = paths.ok_or(Error::CannotFindBinaryPath)?;
+ let paths: Vec<_> = env::split_paths(&p).collect();
+
+ let matching_re = paths
+ .into_iter()
+ .flat_map(fs::read_dir)
+ .flatten()
+ .flatten()
+ .map(|e| e.path())
+ .filter(move |p| {
+ if let Some(unicode_file_name) = p.file_name().unwrap().to_str() {
+ binary_regex.is_match(unicode_file_name)
+ } else {
+ false
+ }
+ })
+ .filter(move |p| binary_checker.is_valid(p));
+
+ Ok(matching_re)
+ }
+
fn cwd_search_candidates<C>(binary_name: PathBuf, cwd: C) -> impl IntoIterator<Item = PathBuf>
where
C: AsRef<Path>,
@@ -110,19 +143,35 @@ impl Finder {
where
P: IntoIterator<Item = PathBuf>,
{
- // Read PATHEXT env variable and split it into vector of String
- let path_exts =
- env::var_os("PATHEXT").unwrap_or(OsString::from(env::consts::EXE_EXTENSION));
-
- let exe_extension_vec = env::split_paths(&path_exts)
- .filter_map(|e| e.to_str().map(|e| e.to_owned()))
- .collect::<Vec<_>>();
+ // Sample %PATHEXT%: .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
+ // PATH_EXTENSIONS is then [".COM", ".EXE", ".BAT", …].
+ // (In one use of PATH_EXTENSIONS we skip the dot, but in the other we need it;
+ // hence its retention.)
+ lazy_static! {
+ static ref PATH_EXTENSIONS: Vec<String> =
+ env::var("PATHEXT")
+ .map(|pathext| {
+ pathext.split(';')
+ .filter_map(|s| {
+ if s.as_bytes()[0] == b'.' {
+ Some(s.to_owned())
+ } else {
+ // Invalid segment; just ignore it.
+ None
+ }
+ })
+ .collect()
+ })
+ // PATHEXT not being set or not being a proper Unicode string is exceedingly
+ // improbable and would probably break Windows badly. Still, don't crash:
+ .unwrap_or(vec![]);
+ }
paths
.into_iter()
.flat_map(move |p| -> Box<dyn Iterator<Item = _>> {
// Check if path already have executable extension
- if has_executable_extension(&p, &exe_extension_vec) {
+ if has_executable_extension(&p, &PATH_EXTENSIONS) {
Box::new(iter::once(p))
} else {
// Appended paths with windows executable extensions.
@@ -131,15 +180,13 @@ impl Finder {
// c:/windows/bin.EXE
// c:/windows/bin.CMD
// ...
- let ps = exe_extension_vec.clone().into_iter().map(move |e| {
+ Box::new(PATH_EXTENSIONS.iter().map(move |e| {
// Append the extension.
- let mut p = p.clone().to_path_buf().into_os_string();
+ let mut p = p.clone().into_os_string();
p.push(e);
PathBuf::from(p)
- });
-
- Box::new(ps)
+ }))
}
})
}
diff --git a/src/helper.rs b/src/helper.rs
index 71658a0..eb96891 100644
--- a/src/helper.rs
+++ b/src/helper.rs
@@ -1,10 +1,10 @@
use std::path::Path;
/// Check if given path has extension which in the given vector.
-pub fn has_executable_extension<T: AsRef<Path>, S: AsRef<str>>(path: T, exts_vec: &Vec<S>) -> bool {
+pub fn has_executable_extension<T: AsRef<Path>, S: AsRef<str>>(path: T, pathext: &[S]) -> bool {
let ext = path.as_ref().extension().and_then(|e| e.to_str());
match ext {
- Some(ext) => exts_vec
+ Some(ext) => pathext
.iter()
.any(|e| ext.eq_ignore_ascii_case(&e.as_ref()[1..])),
_ => false,
@@ -21,12 +21,12 @@ mod test {
// Case insensitive
assert!(has_executable_extension(
PathBuf::from("foo.exe"),
- &vec![".COM", ".EXE", ".CMD"]
+ &[".COM", ".EXE", ".CMD"]
));
assert!(has_executable_extension(
PathBuf::from("foo.CMD"),
- &vec![".COM", ".EXE", ".CMD"]
+ &[".COM", ".EXE", ".CMD"]
));
}
@@ -34,7 +34,7 @@ mod test {
fn test_extension_not_in_extension_vector() {
assert!(!has_executable_extension(
PathBuf::from("foo.bar"),
- &vec![".COM", ".EXE", ".CMD"]
+ &[".COM", ".EXE", ".CMD"]
));
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 4aea3e6..d9377f5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -14,12 +14,18 @@
//!
//! ```
+#[cfg(windows)]
+#[macro_use]
+extern crate lazy_static;
+
mod checker;
mod error;
mod finder;
#[cfg(windows)]
mod helper;
+#[cfg(feature = "regex")]
+use regex::Regex;
use std::env;
use std::fmt;
use std::path;
@@ -62,6 +68,29 @@ pub fn which_all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item =
which_in_all(binary_name, env::var_os("PATH"), cwd)
}
+/// Find all binaries matching a regular expression in a the system PATH.
+///
+/// # Arguments
+///
+/// * `regex` - A regular expression to match binaries with
+///
+/// # Examples
+///
+/// ```no_run
+/// use regex::Regex;
+/// use which::which;
+/// use std::path::PathBuf;
+///
+/// let re = Regex::new(r"python\d$").unwrap();
+/// let binaries: Vec<PathBuf> = which::which_re(re).unwrap().collect();
+/// let python_paths = vec![PathBuf::from("/usr/bin/python2"), PathBuf::from("/usr/bin/python3")];
+/// assert_eq!(binaries, python_paths);
+/// ```
+#[cfg(feature = "regex")]
+pub fn which_re(regex: Regex) -> Result<impl Iterator<Item = path::PathBuf>> {
+ which_re_in(regex, env::var_os("PATH"))
+}
+
/// Find `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
pub fn which_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<path::PathBuf>
where
@@ -73,6 +102,41 @@ where
.and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
}
+/// Find all binaries matching a regular expression in a list of paths.
+///
+/// # Arguments
+///
+/// * `regex` - A regular expression to match binaries with
+/// * `paths` - A string containing the paths to search
+/// (separated in the same way as the PATH environment variable)
+///
+/// # Examples
+///
+/// ```no_run
+/// use regex::Regex;
+/// use which::which;
+/// use std::path::PathBuf;
+///
+/// let re = Regex::new(r"python\d$").unwrap();
+/// let paths = Some("/usr/bin:/usr/local/bin");
+/// let binaries: Vec<PathBuf> = which::which_re_in(re, paths).unwrap().collect();
+/// let python_paths = vec![PathBuf::from("/usr/bin/python2"), PathBuf::from("/usr/bin/python3")];
+/// assert_eq!(binaries, python_paths);
+/// ```
+#[cfg(feature = "regex")]
+pub fn which_re_in<T>(regex: Regex, paths: Option<T>) -> Result<impl Iterator<Item = path::PathBuf>>
+where
+ T: AsRef<OsStr>,
+{
+ let binary_checker = CompositeChecker::new()
+ .add_checker(Box::new(ExistedChecker::new()))
+ .add_checker(Box::new(ExecutableChecker::new()));
+
+ let finder = Finder::new();
+
+ finder.find_re(regex, paths, binary_checker)
+}
+
/// Find all binaries with `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
pub fn which_in_all<T, U, V>(
binary_name: T,
diff --git a/tests/basic.rs b/tests/basic.rs
index 7cb7a08..e3bc73c 100644
--- a/tests/basic.rs
+++ b/tests/basic.rs
@@ -1,11 +1,13 @@
extern crate tempdir;
extern crate which;
-use std::env;
+#[cfg(feature = "regex")]
+use regex::Regex;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
+use std::{env, vec};
use tempdir::TempDir;
struct TestFixture {
@@ -126,6 +128,38 @@ fn test_which() {
}
#[test]
+#[cfg(all(unix, feature = "regex"))]
+fn test_which_re_in_with_matches() {
+ let f = TestFixture::new();
+ f.mk_bin("a/bin_0", "").unwrap();
+ f.mk_bin("b/bin_1", "").unwrap();
+ let re = Regex::new(r"bin_\d").unwrap();
+
+ let result: Vec<PathBuf> = which::which_re_in(re, Some(f.paths))
+ .unwrap()
+ .into_iter()
+ .collect();
+
+ let temp = f.tempdir;
+
+ assert_eq!(result, vec![temp.path().join("a/bin_0"), temp.path().join("b/bin_1")])
+}
+
+#[test]
+#[cfg(all(unix, feature = "regex"))]
+fn test_which_re_in_without_matches() {
+ let f = TestFixture::new();
+ let re = Regex::new(r"bi[^n]").unwrap();
+
+ let result: Vec<PathBuf> = which::which_re_in(re, Some(f.paths))
+ .unwrap()
+ .into_iter()
+ .collect();
+
+ assert_eq!(result, Vec::<PathBuf>::new())
+}
+
+#[test]
#[cfg(unix)]
fn test_which_extension() {
let f = TestFixture::new();