use crate::{Component, Error};
use std::{
env, fs, io,
path::{Path, PathBuf},
};
pub fn resolve_at(component: Component, path: &Path) -> Result<PathBuf, Error> {
let invalid = |source| Error::Invalid {
component,
path: path.to_owned(),
source,
};
let canonical = fs::canonicalize(path).map_err(invalid)?;
if !canonical.is_file() || env::current_exe().is_ok_and(|shell| shell == canonical) {
return Err(Error::Selection {
component,
path: path.to_owned(),
});
}
crate::installation::verify(&canonical, component.name())?;
Ok(canonical)
}
pub fn resolve(component: Component) -> Result<PathBuf, Error> {
if let Some(path) = env::var_os(component.variable()) {
return resolve_at(component, Path::new(&path));
}
let name = format!("{}{}", component.name(), env::consts::EXE_SUFFIX);
let mut candidates = Vec::new();
if let Ok(shell) = env::current_exe()
&& let Some(directory) = shell.parent()
{
candidates.push(directory.join(&name));
}
if let Some(path) = env::var_os("PATH") {
candidates.extend(env::split_paths(&path).map(|directory| directory.join(&name)));
}
if let Some(home) = env::var_os(if cfg!(windows) { "USERPROFILE" } else { "HOME" }) {
candidates.push(PathBuf::from(home).join(".eska/bin").join(name));
}
for candidate in candidates {
match fs::symlink_metadata(&candidate) {
Ok(_) => return resolve_at(component, &candidate),
Err(error) if error.kind() == io::ErrorKind::NotFound => {}
Err(source) => {
return Err(Error::Invalid {
component,
path: candidate,
source,
});
}
}
}
Err(Error::Missing { component })
}