which/
lib.rs

1use std::path::{Path, PathBuf};
2use std::os::unix::prelude::*;
3use std::collections::HashMap;
4
5pub fn which<'a, 'b>(files: &'a [&'a str],
6                     paths: &'a [&'a Path],
7                     match_all: bool,
8                     mut result: Option<&'b mut HashMap<&'a str, Vec<PathBuf>>>) -> Result<bool, String> {
9    let mut all_matched = true;
10    for f in files {
11        let mut matched = false;
12
13        for p in paths {
14            let mut target = p.to_path_buf();
15            target.push(f);
16
17            // file not exists
18            if !target.exists() {
19                continue;
20            }
21
22            let metadata =
23                target.metadata().map_err(|e| format!("read metadata failed: {}", e))?;
24            if metadata.mode() & 0o111 == 0 {
25                // Not an executable file
26                continue;
27            }
28
29            // Find an executable file
30            matched = true;
31            match result {
32                Some(ref mut r) => {
33                    r.entry(f).or_insert(Vec::new());
34                    if let Some(targets) = r.get_mut(f) {
35                        targets.push(target);
36                    }
37                }
38                None => {}
39            }
40
41            if !match_all {
42                break;
43            }
44        }
45
46        all_matched &= matched;
47    }
48
49    Ok(all_matched)
50}