temp_deno_which 0.1.0

Temp fork with Sys impl of which crate until a new version is published
Documentation
use crate::finder::Checker;
use crate::sys::Sys;
use crate::sys::SysMetadata;
use crate::{NonFatalError, NonFatalErrorHandler};
use std::path::Path;

pub struct ExecutableChecker<TSys: Sys> {
    sys: TSys,
}

impl<TSys: Sys> ExecutableChecker<TSys> {
    pub fn new(sys: TSys) -> Self {
        Self { sys }
    }
}

impl<TSys: Sys> Checker for ExecutableChecker<TSys> {
    fn is_valid<F: NonFatalErrorHandler>(
        &self,
        path: &Path,
        nonfatal_error_handler: &mut F,
    ) -> bool {
        if self.sys.is_windows() && path.extension().is_some() {
            true
        } else {
            let ret = self
                .sys
                .is_valid_executable(path)
                .map_err(|e| nonfatal_error_handler.handle(NonFatalError::Io(e)))
                .unwrap_or(false);
            #[cfg(feature = "tracing")]
            tracing::trace!("{} EXEC_OK = {ret}", path.display());
            ret
        }
    }
}

pub struct ExistedChecker<TSys: Sys> {
    sys: TSys,
}

impl<TSys: Sys> ExistedChecker<TSys> {
    pub fn new(sys: TSys) -> Self {
        Self { sys }
    }
}

impl<TSys: Sys> Checker for ExistedChecker<TSys> {
    fn is_valid<F: NonFatalErrorHandler>(
        &self,
        path: &Path,
        nonfatal_error_handler: &mut F,
    ) -> bool {
        if self.sys.is_windows() {
            let ret = self
                .sys
                .symlink_metadata(path)
                .map(|metadata| {
                    #[cfg(feature = "tracing")]
                    tracing::trace!(
                        "{} is_file() = {}, is_symlink() = {}",
                        path.display(),
                        metadata.is_file(),
                        metadata.is_symlink()
                    );
                    metadata.is_file() || metadata.is_symlink()
                })
                .map_err(|e| {
                    nonfatal_error_handler.handle(NonFatalError::Io(e));
                })
                .unwrap_or(false);
            #[cfg(feature = "tracing")]
            tracing::trace!(
                "{} has_extension = {}, ExistedChecker::is_valid() = {ret}",
                path.display(),
                path.extension().is_some()
            );
            ret
        } else {
            let ret = self.sys.metadata(path).map(|metadata| metadata.is_file());
            #[cfg(feature = "tracing")]
            tracing::trace!("{} is_file() = {ret:?}", path.display());
            match ret {
                Ok(ret) => ret,
                Err(e) => {
                    nonfatal_error_handler.handle(NonFatalError::Io(e));
                    false
                }
            }
        }
    }
}

pub struct CompositeChecker<TSys: Sys> {
    existed_checker: ExistedChecker<TSys>,
    executable_checker: ExecutableChecker<TSys>,
}

impl<TSys: Sys> CompositeChecker<TSys> {
    pub fn new(sys: TSys) -> Self {
        CompositeChecker {
            executable_checker: ExecutableChecker::new(sys.clone()),
            existed_checker: ExistedChecker::new(sys),
        }
    }
}

impl<TSys: Sys> Checker for CompositeChecker<TSys> {
    fn is_valid<F: NonFatalErrorHandler>(
        &self,
        path: &Path,
        nonfatal_error_handler: &mut F,
    ) -> bool {
        self.existed_checker.is_valid(path, nonfatal_error_handler)
            && self
                .executable_checker
                .is_valid(path, nonfatal_error_handler)
    }
}