1use crate::finder::Checker;
2use crate::{NonFatalError, NonFatalErrorHandler};
3use std::fs;
4use std::path::Path;
5
6pub struct ExecutableChecker;
7
8impl ExecutableChecker {
9 pub fn new() -> ExecutableChecker {
10 ExecutableChecker
11 }
12}
13
14impl Checker for ExecutableChecker {
15 #[cfg(any(unix, target_os = "wasi", target_os = "redox"))]
16 fn is_valid<F: NonFatalErrorHandler>(
17 &self,
18 path: &Path,
19 nonfatal_error_handler: &mut F,
20 ) -> bool {
21 use std::io;
22
23 use rustix::fs as rfs;
24 let ret = rfs::access(path, rfs::Access::EXEC_OK)
25 .map_err(|e| {
26 nonfatal_error_handler.handle(NonFatalError::Io(io::Error::from_raw_os_error(
27 e.raw_os_error(),
28 )))
29 })
30 .is_ok();
31 #[cfg(feature = "tracing")]
32 tracing::trace!("{} EXEC_OK = {ret}", path.display());
33 ret
34 }
35
36 #[cfg(windows)]
37 fn is_valid<F: NonFatalErrorHandler>(
38 &self,
39 _path: &Path,
40 _nonfatal_error_handler: &mut F,
41 ) -> bool {
42 true
43 }
44}
45
46pub struct ExistedChecker;
47
48impl ExistedChecker {
49 pub fn new() -> ExistedChecker {
50 ExistedChecker
51 }
52}
53
54impl Checker for ExistedChecker {
55 #[cfg(target_os = "windows")]
56 fn is_valid<F: NonFatalErrorHandler>(
57 &self,
58 path: &Path,
59 nonfatal_error_handler: &mut F,
60 ) -> bool {
61 let ret = fs::symlink_metadata(path)
62 .map(|metadata| {
63 let file_type = metadata.file_type();
64 #[cfg(feature = "tracing")]
65 tracing::trace!(
66 "{} is_file() = {}, is_symlink() = {}",
67 path.display(),
68 file_type.is_file(),
69 file_type.is_symlink()
70 );
71 file_type.is_file() || file_type.is_symlink()
72 })
73 .map_err(|e| {
74 nonfatal_error_handler.handle(NonFatalError::Io(e));
75 })
76 .unwrap_or(false)
77 && (path.extension().is_some() || matches_arch(path, nonfatal_error_handler));
78 #[cfg(feature = "tracing")]
79 tracing::trace!(
80 "{} has_extension = {}, ExistedChecker::is_valid() = {ret}",
81 path.display(),
82 path.extension().is_some()
83 );
84 ret
85 }
86
87 #[cfg(not(target_os = "windows"))]
88 fn is_valid<F: NonFatalErrorHandler>(
89 &self,
90 path: &Path,
91 nonfatal_error_handler: &mut F,
92 ) -> bool {
93 let ret = fs::metadata(path).map(|metadata| metadata.is_file());
94 #[cfg(feature = "tracing")]
95 tracing::trace!("{} is_file() = {ret:?}", path.display());
96 match ret {
97 Ok(ret) => ret,
98 Err(e) => {
99 nonfatal_error_handler.handle(NonFatalError::Io(e));
100 false
101 }
102 }
103 }
104}
105
106#[cfg(target_os = "windows")]
107fn matches_arch<F: NonFatalErrorHandler>(path: &Path, nonfatal_error_handler: &mut F) -> bool {
108 use std::io;
109
110 let ret = winsafe::GetBinaryType(&path.display().to_string())
111 .map_err(|e| {
112 nonfatal_error_handler.handle(NonFatalError::Io(io::Error::from_raw_os_error(
113 e.raw() as i32
114 )))
115 })
116 .is_ok();
117 #[cfg(feature = "tracing")]
118 tracing::trace!("{} matches_arch() = {ret}", path.display());
119 ret
120}
121
122pub struct CompositeChecker {
123 existed_checker: ExistedChecker,
124 executable_checker: ExecutableChecker,
125}
126
127impl CompositeChecker {
128 pub fn new() -> CompositeChecker {
129 CompositeChecker {
130 executable_checker: ExecutableChecker::new(),
131 existed_checker: ExistedChecker::new(),
132 }
133 }
134}
135
136impl Checker for CompositeChecker {
137 fn is_valid<F: NonFatalErrorHandler>(
138 &self,
139 path: &Path,
140 nonfatal_error_handler: &mut F,
141 ) -> bool {
142 self.existed_checker.is_valid(path, nonfatal_error_handler)
143 && self
144 .executable_checker
145 .is_valid(path, nonfatal_error_handler)
146 }
147}