eska_tool_runtime/
resolver.rs1use crate::{Component, Error};
4use std::{
5 env, fs, io,
6 path::{Path, PathBuf},
7};
8
9pub fn resolve_at(component: Component, path: &Path) -> Result<PathBuf, Error> {
14 let invalid = |source| Error::Invalid {
15 component,
16 path: path.to_owned(),
17 source,
18 };
19 let canonical = fs::canonicalize(path).map_err(invalid)?;
20 if !canonical.is_file() || env::current_exe().is_ok_and(|shell| shell == canonical) {
21 return Err(Error::Selection {
22 component,
23 path: path.to_owned(),
24 });
25 }
26 crate::installation::verify(&canonical, component.name())?;
27 Ok(canonical)
28}
29
30pub fn resolve(component: Component) -> Result<PathBuf, Error> {
35 if let Some(path) = env::var_os(component.variable()) {
36 return resolve_at(component, Path::new(&path));
37 }
38 let name = format!("{}{}", component.name(), env::consts::EXE_SUFFIX);
39 let mut candidates = Vec::new();
40 if let Ok(shell) = env::current_exe()
41 && let Some(directory) = shell.parent()
42 {
43 candidates.push(directory.join(&name));
44 }
45 if let Some(path) = env::var_os("PATH") {
46 candidates.extend(env::split_paths(&path).map(|directory| directory.join(&name)));
47 }
48 if let Some(home) = env::var_os(if cfg!(windows) { "USERPROFILE" } else { "HOME" }) {
49 candidates.push(PathBuf::from(home).join(".eska/bin").join(name));
50 }
51 for candidate in candidates {
52 match fs::symlink_metadata(&candidate) {
53 Ok(_) => return resolve_at(component, &candidate),
54 Err(error) if error.kind() == io::ErrorKind::NotFound => {}
55 Err(source) => {
56 return Err(Error::Invalid {
57 component,
58 path: candidate,
59 source,
60 });
61 }
62 }
63 }
64 Err(Error::Missing { component })
65}