1use crate::sys::Sys;
2use crate::sys::SysMetadata;
3use crate::{NonFatalError, NonFatalErrorHandler};
4use std::path::Path;
5
6pub fn is_valid<F: NonFatalErrorHandler>(
7 sys: impl Sys,
8 path: &Path,
9 nonfatal_error_handler: &mut F,
10) -> bool {
11 exists(&sys, path, nonfatal_error_handler) && is_executable(&sys, path, nonfatal_error_handler)
12}
13
14fn is_executable<F: NonFatalErrorHandler>(
15 sys: impl Sys,
16 path: &Path,
17 nonfatal_error_handler: &mut F,
18) -> bool {
19 if sys.is_windows() && path.extension().is_some() {
20 true
21 } else {
22 let ret = sys
23 .is_valid_executable(path)
24 .map_err(|e| nonfatal_error_handler.handle(NonFatalError::Io(e)))
25 .unwrap_or(false);
26 #[cfg(feature = "tracing")]
27 tracing::trace!("{} EXEC_OK = {ret}", path.display());
28 ret
29 }
30}
31
32fn exists<F: NonFatalErrorHandler>(
33 sys: impl Sys,
34 path: &Path,
35 nonfatal_error_handler: &mut F,
36) -> bool {
37 {
38 if sys.is_windows() {
39 let ret = sys
40 .symlink_metadata(path)
41 .map(|metadata| {
42 #[cfg(feature = "tracing")]
43 tracing::trace!(
44 "{} is_file() = {}, is_symlink() = {}",
45 path.display(),
46 metadata.is_file(),
47 metadata.is_symlink()
48 );
49 metadata.is_file() || metadata.is_symlink()
50 })
51 .map_err(|e| {
52 nonfatal_error_handler.handle(NonFatalError::Io(e));
53 })
54 .unwrap_or(false);
55 #[cfg(feature = "tracing")]
56 tracing::trace!(
57 "{} has_extension = {}, checker::exists() = {ret}",
58 path.display(),
59 path.extension().is_some()
60 );
61 ret
62 } else {
63 let ret = sys.metadata(path).map(|metadata| metadata.is_file());
64 #[cfg(feature = "tracing")]
65 tracing::trace!("{} is_file() = {ret:?}", path.display());
66 match ret {
67 Ok(ret) => ret,
68 Err(e) => {
69 nonfatal_error_handler.handle(NonFatalError::Io(e));
70 false
71 }
72 }
73 }
74 }
75}