use std::env;
use std::path::PathBuf;
use color_eyre::Result;
use color_eyre::eyre;
use crate::lfs;
use crate::models::*;
use crate::dirs::{get_env_root, path_join};
use crate::environment::registered_env_configs;
use crate::shell_emit;
pub fn update_path() -> Result<()> {
let sep = shell_emit::path_sep_os();
let shell_kind = shell_emit::detect();
let mut path_components = Vec::new();
let mut pure = false;
if let Ok(active_env) = env::var("EPKG_ACTIVE_ENV") {
let active_envs: Vec<&str> = active_env.split(':').collect();
for env_name in active_envs.iter() {
let (env_name, is_pure) = if env_name.ends_with(PURE_ENV_SUFFIX) {
(env_name[..env_name.len() - 1].to_string(), true)
} else {
(env_name.to_string(), false)
};
pure = pure && is_pure;
path_components.extend(get_active_env_paths(&env_name, is_pure)?);
}
}
if !pure {
path_components.extend(get_registered_env_paths()?);
}
let mut seen = std::collections::HashSet::new();
path_components.retain(|item| seen.insert(item.clone()));
let self_bin = self_usr_bin_path()?;
if let Some(ref p) = self_bin {
path_components.retain(|x| x != p);
}
if path_components.is_empty() && self_bin.is_none() {
return Err(eyre::eyre!("No valid paths found to update PATH"));
}
let mut new_path = path_components.join(&sep.to_string());
if let Some(p) = self_bin {
if !new_path.is_empty() {
new_path.push(sep);
}
new_path.push_str(&p);
}
env::set_var("PATH", &new_path);
println!("{}", shell_emit::emit_path(&new_path, shell_kind));
Ok(())
}
fn self_usr_bin_path() -> Result<Option<String>> {
let root = get_env_root(SELF_ENV.to_string())?;
let p = path_join(&root, &["usr", "bin"]);
if lfs::exists_or_any_symlink(&p) {
Ok(Some(p.display().to_string()))
} else {
Ok(None)
}
}
fn get_active_env_paths(active_env: &str, pure: bool) -> Result<Vec<String>> {
let mut path_components = Vec::new();
let env_root = get_env_root(active_env.to_string())?;
if !lfs::exists_or_any_symlink(&env_root) {
return Err(eyre::eyre!("Active environment '{}' does not exist", active_env));
}
let ebin_path = env_root.join("ebin");
if lfs::exists_or_any_symlink(&ebin_path) {
path_components.push(ebin_path.display().to_string());
}
if pure {
let bin_path = crate::dirs::path_join(&env_root, &["usr", "bin"]);
let sbin_path = crate::dirs::path_join(&env_root, &["usr", "sbin"]);
if lfs::exists_or_any_symlink(&bin_path) {
path_components.push(bin_path.display().to_string());
}
if lfs::exists_or_any_symlink(&sbin_path) {
path_components.push(sbin_path.display().to_string());
}
}
Ok(path_components)
}
fn get_registered_env_paths() -> Result<Vec<String>> {
let mut path_components = Vec::new();
let mut prepend: Vec<(i32, String, String)> = Vec::new();
let mut append: Vec<(i32, String, String)> = Vec::new();
let shared_store = config().init.shared_store;
let configs = registered_env_configs(shared_store);
for config in configs {
let env_root = PathBuf::from(&config.env_root);
let ebin_path = env_root.join("ebin");
if !lfs::exists_or_any_symlink(&ebin_path) {
continue;
}
let path_str = ebin_path.display().to_string();
let entry = (config.register_path_order, config.name.clone(), path_str);
if config.register_path_order >= 0 {
prepend.push(entry);
} else {
append.push(entry);
}
}
prepend.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
append.sort_by(|a, b| a.0.abs().cmp(&b.0.abs()).then(a.1.cmp(&b.1)));
path_components.extend(prepend.into_iter().map(|(_, _, path)| path));
path_components.extend(get_system_paths()?);
path_components.extend(append.into_iter().map(|(_, _, path)| path));
Ok(path_components)
}
fn get_system_paths() -> Result<Vec<String>> {
let mut path_components = Vec::new();
let sep = shell_emit::path_sep_os();
if let Ok(path) = env::var("PATH") {
path_components.extend(
path
.split(sep)
.filter(|dir| !dir.is_empty() && !dir.contains("epkg"))
.map(String::from),
);
}
Ok(path_components)
}