which/
checker.rs

1use crate::finder::Checker;
2use std::fs;
3use std::path::Path;
4
5pub struct ExecutableChecker;
6
7impl ExecutableChecker {
8    pub fn new() -> ExecutableChecker {
9        ExecutableChecker
10    }
11}
12
13impl Checker for ExecutableChecker {
14    #[cfg(any(unix, target_os = "wasi"))]
15    fn is_valid(&self, path: &Path) -> bool {
16        use rustix::fs as rfs;
17        rfs::access(path, rfs::Access::EXEC_OK).is_ok()
18    }
19
20    #[cfg(windows)]
21    fn is_valid(&self, _path: &Path) -> bool {
22        true
23    }
24}
25
26pub struct ExistedChecker;
27
28impl ExistedChecker {
29    pub fn new() -> ExistedChecker {
30        ExistedChecker
31    }
32}
33
34impl Checker for ExistedChecker {
35    #[cfg(target_os = "windows")]
36    fn is_valid(&self, path: &Path) -> bool {
37        fs::symlink_metadata(path)
38            .map(|metadata| {
39                let file_type = metadata.file_type();
40                file_type.is_file() || file_type.is_symlink()
41            })
42            .unwrap_or(false)
43            && (path.extension().is_some() || matches_arch(path))
44    }
45
46    #[cfg(not(target_os = "windows"))]
47    fn is_valid(&self, path: &Path) -> bool {
48        fs::metadata(path)
49            .map(|metadata| metadata.is_file())
50            .unwrap_or(false)
51    }
52}
53
54#[cfg(target_os = "windows")]
55fn matches_arch(path: &Path) -> bool {
56    use std::os::windows::prelude::OsStrExt;
57
58    let os_str = path
59        .as_os_str()
60        .encode_wide()
61        .chain(std::iter::once(0))
62        .collect::<Vec<u16>>();
63    let mut out = 0;
64    let is_executable = unsafe {
65        windows_sys::Win32::Storage::FileSystem::GetBinaryTypeW(os_str.as_ptr(), &mut out)
66    };
67    is_executable != 0
68}
69
70pub struct CompositeChecker {
71    checkers: Vec<Box<dyn Checker>>,
72}
73
74impl CompositeChecker {
75    pub fn new() -> CompositeChecker {
76        CompositeChecker {
77            checkers: Vec::new(),
78        }
79    }
80
81    pub fn add_checker(mut self, checker: Box<dyn Checker>) -> CompositeChecker {
82        self.checkers.push(checker);
83        self
84    }
85}
86
87impl Checker for CompositeChecker {
88    fn is_valid(&self, path: &Path) -> bool {
89        self.checkers.iter().all(|checker| checker.is_valid(path))
90    }
91}