Skip to content

Commit

Permalink
Implement extension-less executable finding on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
Xaeroxe committed Oct 17, 2023
1 parent 35196c1 commit b2b8068
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 17 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ rustix = { version = "0.38.10", default-features = false, features = ["fs", "std
home = "0.5.5"

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.48", features = ["Win32_Storage_FileSystem", "Win32_Foundation"] }
once_cell = "1"

[dev-dependencies]
Expand Down
13 changes: 13 additions & 0 deletions src/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ impl Checker for ExistedChecker {
file_type.is_file() || file_type.is_symlink()
})
.unwrap_or(false)
&& (path.extension().is_some() || matches_arch(&path))
}

#[cfg(not(target_os = "windows"))]
Expand All @@ -50,6 +51,18 @@ impl Checker for ExistedChecker {
}
}

#[cfg(target_os = "windows")]
fn matches_arch(path: &Path) -> bool {
use std::os::windows::prelude::OsStrExt;

let os_str = path.as_os_str().encode_wide().collect::<Vec<u16>>();
let mut out = 0;
let is_executable = unsafe {
windows_sys::Win32::Storage::FileSystem::GetBinaryTypeW(os_str.as_ptr(), &mut out)
};
is_executable != 0
}

pub struct CompositeChecker {
checkers: Vec<Box<dyn Checker>>,
}
Expand Down
14 changes: 8 additions & 6 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ pub type Result<T> = std::result::Result<T, Error>;

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Error {
BadAbsolutePath,
BadRelativePath,
/// An executable binary with that name was not found
CannotFindBinaryPath,
CannotGetCurrentDir,
/// There was nowhere to search and the provided name wasn't an absolute path
CannotGetCurrentDirAndPathListEmpty,
/// Failed to canonicalize the path found
CannotCanonicalize,
}

Expand All @@ -16,10 +17,11 @@ impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::BadAbsolutePath => write!(f, "bad absolute path"),
Error::BadRelativePath => write!(f, "bad relative path"),
Error::CannotFindBinaryPath => write!(f, "cannot find binary path"),
Error::CannotGetCurrentDir => write!(f, "cannot get current directory"),
Error::CannotGetCurrentDirAndPathListEmpty => write!(
f,
"no path to search and provided name is not an absolute path"
),
Error::CannotCanonicalize => write!(f, "cannot canonicalize path"),
}
}
Expand Down
15 changes: 6 additions & 9 deletions src/finder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,6 @@ impl Finder {
if has_executable_extension(&p, &PATH_EXTENSIONS) {
Box::new(iter::once(p))
} else {
let bare_file = p.extension().map(|_| p.clone());
// Appended paths with windows executable extensions.
// e.g. path `c:/windows/bin[.ext]` will expand to:
// [c:/windows/bin.ext]
Expand All @@ -207,15 +206,13 @@ impl Finder {
// c:/windows/bin[.ext].CMD
// ...
Box::new(
bare_file
.into_iter()
.chain(PATH_EXTENSIONS.iter().map(move |e| {
// Append the extension.
let mut p = p.clone().into_os_string();
p.push(e);
iter::once(p.clone()).chain(PATH_EXTENSIONS.iter().map(move |e| {
// Append the extension.
let mut p = p.clone().into_os_string();
p.push(e);

PathBuf::from(p)
})),
PathBuf::from(p)
})),
)
}
})
Expand Down
2 changes: 0 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
//!
//! ```

#![forbid(unsafe_code)]

mod checker;
mod error;
mod finder;
Expand Down

0 comments on commit b2b8068

Please sign in to comment.