use crate::{env, timeout};
use heck::ToKebabCase;
use std::collections::{BTreeMap, HashMap};
use std::fmt::Debug;
use std::path::PathBuf;
use std::sync::RwLock;
use std::thread;
use crate::backend::backend_type::BackendType;
use crate::backend::Backend;
use crate::cache::{CacheManager, CacheManagerBuilder};
use crate::cli::args::BackendArg;
use crate::config::{Config, SETTINGS};
use crate::dirs;
use crate::env_diff::EnvMap;
use crate::install_context::InstallContext;
use crate::plugins::vfox_plugin::VfoxPlugin;
use crate::plugins::{Plugin, PluginType};
use crate::tokio::RUNTIME;
use crate::toolset::{ToolVersion, Toolset};
use crate::ui::multi_progress_report::MultiProgressReport;
#[derive(Debug)]
pub struct VfoxBackend {
ba: BackendArg,
plugin: Box<VfoxPlugin>,
exec_env_cache: RwLock<HashMap<String, CacheManager<EnvMap>>>,
pathname: String,
}
impl Backend for VfoxBackend {
fn get_type(&self) -> BackendType {
BackendType::Vfox
}
fn ba(&self) -> &BackendArg {
&self.ba
}
fn get_plugin_type(&self) -> Option<PluginType> {
Some(PluginType::Vfox)
}
fn _list_remote_versions(&self) -> eyre::Result<Vec<String>> {
timeout::run_with_timeout(
|| {
let (vfox, _log_rx) = self.plugin.vfox();
self.ensure_plugin_installed()?;
let versions = RUNTIME.block_on(vfox.list_available_versions(&self.pathname))?;
Ok(versions
.into_iter()
.rev()
.map(|v| v.version)
.collect::<Vec<String>>())
},
SETTINGS.fetch_remote_versions_timeout(),
)
}
fn install_version_(
&self,
_ctx: &InstallContext,
tv: ToolVersion,
) -> eyre::Result<ToolVersion> {
self.ensure_plugin_installed()?;
let (vfox, log_rx) = self.plugin.vfox();
thread::spawn(|| {
for line in log_rx {
info!("{}", line);
}
});
RUNTIME.block_on(vfox.install(&self.pathname, &tv.version, tv.install_path()))?;
Ok(tv)
}
fn list_bin_paths(&self, tv: &ToolVersion) -> eyre::Result<Vec<PathBuf>> {
let path = self
._exec_env(tv)?
.iter()
.find(|(k, _)| k.to_uppercase() == "PATH")
.map(|(_, v)| v.to_string())
.unwrap_or("bin".to_string());
Ok(env::split_paths(&path).collect())
}
fn exec_env(&self, _config: &Config, _ts: &Toolset, tv: &ToolVersion) -> eyre::Result<EnvMap> {
Ok(self
._exec_env(tv)?
.into_iter()
.filter(|(k, _)| k.to_uppercase() != "PATH")
.collect())
}
fn plugin(&self) -> Option<&dyn Plugin> {
Some(&*self.plugin)
}
}
impl VfoxBackend {
pub fn from_arg(ba: BackendArg) -> Self {
let pathname = ba.short.to_kebab_case();
let plugin_path = dirs::PLUGINS.join(&pathname);
let mut plugin = VfoxPlugin::new(pathname.clone(), plugin_path.clone());
plugin.full = Some(ba.full());
Self {
exec_env_cache: Default::default(),
plugin: Box::new(plugin),
ba,
pathname,
}
}
fn _exec_env(&self, tv: &ToolVersion) -> eyre::Result<BTreeMap<String, String>> {
let key = tv.to_string();
if !self.exec_env_cache.read().unwrap().contains_key(&key) {
let mut caches = self.exec_env_cache.write().unwrap();
caches.insert(
key.clone(),
CacheManagerBuilder::new(tv.cache_path().join("exec_env.msgpack.z"))
.with_fresh_file(dirs::DATA.to_path_buf())
.with_fresh_file(self.plugin.plugin_path.to_path_buf())
.with_fresh_file(self.ba().installs_path.to_path_buf())
.build(),
);
}
let exec_env_cache = self.exec_env_cache.read().unwrap();
let cache = exec_env_cache.get(&key).unwrap();
cache
.get_or_try_init(|| {
self.ensure_plugin_installed()?;
let (vfox, _log_rx) = self.plugin.vfox();
Ok(RUNTIME
.block_on(vfox.env_keys(&self.pathname, &tv.version))?
.into_iter()
.fold(BTreeMap::new(), |mut acc, env_key| {
let key = &env_key.key;
if let Some(val) = acc.get(key) {
let mut paths = env::split_paths(val).collect::<Vec<PathBuf>>();
paths.push(PathBuf::from(env_key.value));
acc.insert(
env_key.key,
env::join_paths(paths)
.unwrap()
.to_string_lossy()
.to_string(),
);
} else {
acc.insert(key.clone(), env_key.value);
}
acc
}))
})
.cloned()
}
fn ensure_plugin_installed(&self) -> eyre::Result<()> {
self.plugin
.ensure_installed(&MultiProgressReport::get(), false)
}
}