use std::path::{Path, PathBuf};
use std::{fs, io};
pub fn entries(bin: &Path) -> io::Result<impl Iterator<Item = (PathBuf, PathBuf)>> {
Ok(bin
.read_dir()?
.filter_map(Result::ok)
.map(|entry| entry.path())
.filter_map(|sym| sym.read_link().map(|dest| (sym, dest)).ok()))
}
pub fn iterate(
bin: &Path,
repos: Vec<PathBuf>,
) -> io::Result<impl Iterator<Item = (PathBuf, PathBuf)>> {
Ok(bin
.read_dir()?
.filter_map(Result::ok)
.map(|entry| entry.path())
.filter_map(|sym| sym.read_link().map(|dest| (sym, dest)).ok())
.filter(move |(_, dest)| repos.iter().any(|repo| dest.starts_with(repo))))
}
pub fn link_path(bin: &Path, exe: &Path) -> io::Result<PathBuf> {
exe.file_name()
.map(|osname| bin.join(osname))
.ok_or(io::Error::new(
io::ErrorKind::NotFound,
"no file name found",
))
}
pub fn create(bin: &Path, exe: &Path) -> io::Result<PathBuf> {
fs::create_dir_all(bin).ok();
let sym = link_path(bin, exe)?;
{
#[cfg(target_family = "unix")]
{
use std::os::unix::fs::symlink;
symlink(exe, &sym)
}
#[cfg(target_family = "windows")]
{
use std::os::windows::fs::symlink_file;
symlink_file(exe, &sym)
}
}
.map(|_| sym)
}
pub fn deref_rec(path: &Path) -> PathBuf {
match path.read_link() {
Ok(dest) => deref_rec(&match dest.is_relative() {
false => dest,
true => path.parent().unwrap().join(dest),
}),
Err(_) => path.into(),
}
}
fn is_executable(path: &Path) -> bool {
let path = deref_rec(path);
path.is_file() && {
#[cfg(target_family = "unix")]
{
use std::os::unix::fs::MetadataExt;
path.metadata()
.map(|meta| meta.mode() & 0o100 > 0)
.unwrap_or(false)
}
#[cfg(target_family = "windows")]
{
path.extension()
.map(|ostyp| ostyp.to_str().map(|typ| typ == "exe" || typ == "bat"))
.flatten()
.unwrap_or(false)
}
}
}
pub fn executables(dir: &Path) -> io::Result<impl Iterator<Item = PathBuf>> {
Ok(dir
.read_dir()?
.filter_map(Result::ok)
.map(|entry| entry.path())
.filter(|path| is_executable(path)))
}