diff options
author | Joel Galenson <jgalenson@google.com> | 2021-08-13 03:12:43 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-08-13 03:12:43 +0000 |
commit | ff8245d7b3988f916e42a6d6ca3183b852e85f4a (patch) | |
tree | ee0741f958fe697978838edb95ab00e50af08beb | |
parent | d1f21a518da6259761da0e31870c0910bfb767ee (diff) | |
parent | c0a4e4addc7f57818a04cde4594a0b5867e6ceb2 (diff) | |
download | which-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.json | 2 | ||||
-rw-r--r-- | .github/workflows/rust.yml | 19 | ||||
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Android.bp | 2 | ||||
-rw-r--r-- | Cargo.toml | 8 | ||||
-rw-r--r-- | Cargo.toml.orig | 6 | ||||
-rw-r--r-- | METADATA | 8 | ||||
-rw-r--r-- | src/finder.rs | 79 | ||||
-rw-r--r-- | src/helper.rs | 10 | ||||
-rw-r--r-- | src/lib.rs | 64 | ||||
-rw-r--r-- | tests/basic.rs | 36 |
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 @@ -1,2 +1,3 @@ target Cargo.lock +.vscode/ @@ -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" @@ -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" @@ -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"] )); } } @@ -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(); |