which/
checker.rs

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}