diff options
-rw-r--r-- | .cargo_vcs_info.json | 7 | ||||
-rw-r--r-- | .github/workflows/rust.yml | 97 | ||||
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Android.bp | 8 | ||||
-rw-r--r-- | Cargo.toml | 19 | ||||
-rw-r--r-- | Cargo.toml.orig | 9 | ||||
-rw-r--r-- | METADATA | 10 | ||||
-rw-r--r-- | README.md | 23 | ||||
-rw-r--r-- | TEST_MAPPING | 14 | ||||
-rw-r--r-- | cargo2android.json | 5 | ||||
-rw-r--r-- | src/finder.rs | 104 | ||||
-rw-r--r-- | src/helper.rs | 10 | ||||
-rw-r--r-- | src/lib.rs | 105 | ||||
-rw-r--r-- | tests/basic.rs | 48 |
14 files changed, 84 insertions, 376 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 692ca11..fb1169d 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,5 @@ { "git": { - "sha1": "8fbe34239c16af9cd253e36e9c2d3384f9b55f83" - }, - "path_in_vcs": "" -}
\ No newline at end of file + "sha1": "2c54067bab846e96f07be60e78250f385825f09f" + } +} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index dd380d3..288ccaf 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,93 +1,28 @@ -name: Main workflow +name: Rust + on: push: + branches: [ master ] pull_request: + branches: [ master ] -jobs: - # Run the `rustfmt` code formatter - rustfmt: - name: Rustfmt [Formatter] - runs-on: ubuntu-latest - steps: - - name: Setup | Checkout - uses: actions/checkout@v2 - - - name: Setup | Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - profile: minimal - components: rustfmt - - - name: Build | Format - run: cargo fmt --all -- --check - - # Run the `clippy` linting tool - clippy: - name: Clippy [Linter] - strategy: - matrix: - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Setup | Checkout - uses: actions/checkout@v2 +env: + CARGO_TERM_COLOR: always - - name: Setup | Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - profile: minimal - components: clippy - - - name: Build | Lint - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --workspace --all-targets --all-features - - # Ensure that the project could be successfully compiled - cargo_check: - name: Compile - runs-on: ubuntu-latest - steps: - - name: Setup | Checkout - uses: actions/checkout@v2 - - - name: Setup | Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - - - name: Build | Check - run: cargo check --workspace +jobs: - # Run tests on Linux, macOS, and Windows - # On both Rust stable and Rust nightly test: - name: Test Suite + name: Build and test runs-on: ${{ matrix.os }} - needs: cargo_check # First check then run expansive tests strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] - rust: [stable, nightly] - steps: - - name: Setup | Checkout - uses: actions/checkout@v2 + os: + - ubuntu-latest + - windows-latest + - macos-latest - - name: Setup | Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - profile: minimal - override: true - - # Run the ignored tests that expect the above setup - - name: Build | Test - run: cargo test --workspace --all-features -- -Z unstable-options --include-ignored + steps: + - uses: actions/checkout@v2 + - run: cargo build --verbose + - run: cargo test --verbose @@ -1,3 +1,2 @@ target Cargo.lock -.vscode/ @@ -1,4 +1,4 @@ -// This file is generated by cargo2android.py --config cargo2android.json. +// This file is generated by cargo2android.py --run --dependencies --device --features=. // Do not modify this file as changes will be overridden on upgrade. package { @@ -22,8 +22,6 @@ rust_library { name: "libwhich", host_supported: true, crate_name: "which", - cargo_env_compat: true, - cargo_pkg_version: "4.2.4", srcs: ["src/lib.rs"], edition: "2018", rustlibs: [ @@ -31,3 +29,7 @@ rust_library { "liblibc", ], } + +// dependent_library ["feature_list"] +// either-1.6.1 "default,use_std" +// libc-0.2.92 "default,std" @@ -3,16 +3,17 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies. +# to registry (e.g., crates.io) dependencies # -# If you are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) [package] edition = "2018" name = "which" -version = "4.2.4" +version = "4.1.0" 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/" @@ -21,18 +22,10 @@ keywords = ["which", "which-rs", "unix", "command"] categories = ["os", "filesystem"] license = "MIT" repository = "https://github.com/harryfei/which-rs.git" -[package.metadata.docs.rs] -all-features = true [dependencies.either] 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 ec31c55..c3c472e 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "which" -version = "4.2.4" +version = "4.1.0" edition = "2018" authors = ["Harry Fei <tiziyuanfang@gmail.com>"] repository = "https://github.com/harryfei/which-rs.git" @@ -14,13 +14,6 @@ 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" - -[package.metadata.docs.rs] -all-features = true @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/which/which-4.2.4.crate" + value: "https://static.crates.io/crates/which/which-4.1.0.crate" } - version: "4.2.4" + version: "4.1.0" license_type: NOTICE last_upgrade_date { - year: 2022 - month: 3 - day: 1 + year: 2021 + month: 4 + day: 2 } } @@ -10,25 +10,16 @@ A Rust equivalent of Unix command "which". Locate installed executable in cross * Windows * macOS -## Examples +## Example -1) To find which rustc executable binary is using. +To find which rustc exectable binary is using. - ``` rust - use which::which; +``` rust +use which::which; - let result = which("rustc").unwrap(); - assert_eq!(result, PathBuf::from("/usr/bin/rustc")); - ``` - -2. After enabling the `regex` feature, find all cargo subcommand executables on the path: - - ``` rust - use which::which_re; - - which_re(Regex::new("^cargo-.*").unwrap()).unwrap() - .for_each(|pth| println!("{}", pth.to_string_lossy())); - ``` +let result = which::which("rustc").unwrap(); +assert_eq!(result, PathBuf::from("/usr/bin/rustc")); +``` ## Documentation diff --git a/TEST_MAPPING b/TEST_MAPPING index e4ec3b3..df48c8d 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,24 +1,14 @@ // Generated by update_crate_tests.py for tests that depend on this crate. { - "imports": [ - { - "path": "external/rust/crates/libsqlite3-sys" - } - ], "presubmit": [ { - "name": "keystore2_test" + "name": "libsqlite3-sys_device_test_src_lib" }, { - "name": "legacykeystore_test" - } - ], - "presubmit-rust": [ - { "name": "keystore2_test" }, { - "name": "legacykeystore_test" + "name": "vpnprofilestore_test" } ] } diff --git a/cargo2android.json b/cargo2android.json deleted file mode 100644 index 5fd6c55..0000000 --- a/cargo2android.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "device": true, - "features": "", - "run": true -}
\ No newline at end of file diff --git a/src/finder.rs b/src/finder.rs index 43b659d..91c6cab 100644 --- a/src/finder.rs +++ b/src/finder.rs @@ -1,16 +1,12 @@ 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; -#[cfg(feature = "regex")] -use std::borrow::Borrow; use std::env; use std::ffi::OsStr; -#[cfg(feature = "regex")] -use std::fs; +#[cfg(windows)] +use std::ffi::OsString; use std::iter; use std::path::{Path, PathBuf}; @@ -56,7 +52,7 @@ impl Finder { &self, binary_name: T, paths: Option<U>, - cwd: Option<V>, + cwd: V, binary_checker: CompositeChecker, ) -> Result<impl Iterator<Item = PathBuf>> where @@ -66,54 +62,20 @@ impl Finder { { let path = PathBuf::from(&binary_name); - let binary_path_candidates = match cwd { - Some(cwd) if path.has_separator() => { - // Search binary in cwd if the path have a path separator. - Either::Left(Self::cwd_search_candidates(path, cwd).into_iter()) - } - _ => { - // Search binary in PATHs(defined in environment variable). - let p = paths.ok_or(Error::CannotFindBinaryPath)?; - let paths: Vec<_> = env::split_paths(&p).collect(); - - Either::Right(Self::path_search_candidates(path, paths).into_iter()) - } + let binary_path_candidates = if path.has_separator() { + // Search binary in cwd if the path have a path separator. + Either::Left(Self::cwd_search_candidates(path, cwd).into_iter()) + } else { + // Search binary in PATHs(defined in environment variable). + let p = paths.ok_or(Error::CannotFindBinaryPath)?; + let paths: Vec<_> = env::split_paths(&p).collect(); + + Either::Right(Self::path_search_candidates(path, paths).into_iter()) }; Ok(binary_path_candidates.filter(move |p| binary_checker.is_valid(p))) } - #[cfg(feature = "regex")] - pub fn find_re<T>( - &self, - binary_regex: impl Borrow<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.borrow().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>, @@ -148,35 +110,19 @@ impl Finder { where P: IntoIterator<Item = PathBuf>, { - // 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().first() == Some(&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![]); - } + // 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<_>>(); paths .into_iter() .flat_map(move |p| -> Box<dyn Iterator<Item = _>> { // Check if path already have executable extension - if has_executable_extension(&p, &PATH_EXTENSIONS) { + if has_executable_extension(&p, &exe_extension_vec) { Box::new(iter::once(p)) } else { // Appended paths with windows executable extensions. @@ -185,13 +131,15 @@ impl Finder { // c:/windows/bin.EXE // c:/windows/bin.CMD // ... - Box::new(PATH_EXTENSIONS.iter().map(move |e| { + let ps = exe_extension_vec.clone().into_iter().map(move |e| { // Append the extension. - let mut p = p.clone().into_os_string(); + let mut p = p.clone().to_path_buf().into_os_string(); p.push(e); PathBuf::from(p) - })) + }); + + Box::new(ps) } }) } diff --git a/src/helper.rs b/src/helper.rs index eb96891..71658a0 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, pathext: &[S]) -> bool { +pub fn has_executable_extension<T: AsRef<Path>, S: AsRef<str>>(path: T, exts_vec: &Vec<S>) -> bool { let ext = path.as_ref().extension().and_then(|e| e.to_str()); match ext { - Some(ext) => pathext + Some(ext) => exts_vec .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"), - &[".COM", ".EXE", ".CMD"] + &vec![".COM", ".EXE", ".CMD"] )); assert!(has_executable_extension( PathBuf::from("foo.CMD"), - &[".COM", ".EXE", ".CMD"] + &vec![".COM", ".EXE", ".CMD"] )); } @@ -34,7 +34,7 @@ mod test { fn test_extension_not_in_extension_vector() { assert!(!has_executable_extension( PathBuf::from("foo.bar"), - &[".COM", ".EXE", ".CMD"] + &vec![".COM", ".EXE", ".CMD"] )); } } @@ -9,25 +9,17 @@ //! use which::which; //! use std::path::PathBuf; //! -//! let result = which("rustc").unwrap(); +//! let result = which::which("rustc").unwrap(); //! assert_eq!(result, PathBuf::from("/usr/bin/rustc")); //! //! ``` -#[cfg(windows)] -#[macro_use] -extern crate lazy_static; - mod checker; mod error; mod finder; #[cfg(windows)] mod helper; -#[cfg(feature = "regex")] -use regex::Regex; -#[cfg(feature = "regex")] -use std::borrow::Borrow; use std::env; use std::fmt; use std::path; @@ -65,50 +57,9 @@ pub fn which<T: AsRef<OsStr>>(binary_name: T) -> Result<path::PathBuf> { /// Find all binaries with `binary_name` in the path list `paths`, using `cwd` to resolve relative paths. pub fn which_all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item = path::PathBuf>> { - let cwd = env::current_dir().ok(); - - let binary_checker = build_binary_checker(); - - let finder = Finder::new(); - - finder.find(binary_name, env::var_os("PATH"), cwd, binary_checker) -} + let cwd = env::current_dir().map_err(|_| Error::CannotGetCurrentDir)?; -/// Find all binaries matching a regular expression in a the system PATH. -/// -/// Only available when feature `regex` is enabled. -/// -/// # Arguments -/// -/// * `regex` - A regular expression to match binaries with -/// -/// # Examples -/// -/// Find Python executables: -/// -/// ```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); -/// ``` -/// -/// Find all cargo subcommand executables on the path: -/// -/// ``` -/// use which::which_re; -/// use regex::Regex; -/// -/// which_re(Regex::new("^cargo-.*").unwrap()).unwrap() -/// .for_each(|pth| println!("{}", pth.to_string_lossy())); -/// ``` -#[cfg(feature = "regex")] -pub fn which_re(regex: impl Borrow<Regex>) -> Result<impl Iterator<Item = path::PathBuf>> { - which_re_in(regex, env::var_os("PATH")) + which_in_all(binary_name, env::var_os("PATH"), cwd) } /// Find `binary_name` in the path list `paths`, using `cwd` to resolve relative paths. @@ -122,44 +73,6 @@ where .and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath)) } -/// Find all binaries matching a regular expression in a list of paths. -/// -/// Only available when feature `regex` is enabled. -/// -/// # 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: impl Borrow<Regex>, - paths: Option<T>, -) -> Result<impl Iterator<Item = path::PathBuf>> -where - T: AsRef<OsStr>, -{ - let binary_checker = build_binary_checker(); - - 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, @@ -171,17 +84,13 @@ where U: AsRef<OsStr>, V: AsRef<path::Path>, { - let binary_checker = build_binary_checker(); + let binary_checker = CompositeChecker::new() + .add_checker(Box::new(ExistedChecker::new())) + .add_checker(Box::new(ExecutableChecker::new())); let finder = Finder::new(); - finder.find(binary_name, paths, Some(cwd), binary_checker) -} - -fn build_binary_checker() -> CompositeChecker { - CompositeChecker::new() - .add_checker(Box::new(ExistedChecker::new())) - .add_checker(Box::new(ExecutableChecker::new())) + finder.find(binary_name, paths, cwd, binary_checker) } /// An owned, immutable wrapper around a `PathBuf` containing the path of an executable. diff --git a/tests/basic.rs b/tests/basic.rs index 897e912..7cb7a08 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -1,13 +1,11 @@ extern crate tempdir; extern crate which; -#[cfg(feature = "regex")] -use regex::Regex; +use std::env; 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 { @@ -128,50 +126,6 @@ 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(all(unix, feature = "regex"))] -fn test_which_re_accepts_owned_and_borrow() { - which::which_re(Regex::new(r".").unwrap()); - which::which_re(&Regex::new(r".").unwrap()); - which::which_re_in(Regex::new(r".").unwrap(), Some("pth")); - which::which_re_in(&Regex::new(r".").unwrap(), Some("pth")); -} - -#[test] #[cfg(unix)] fn test_which_extension() { let f = TestFixture::new(); |