#[cfg(windows)]
use crate::config::directories::project_dirs;
use crate::error::cache::{
DeleteCacheError, EnsureCacheVersionsDirError, GetBinaryCommandPathError, GetCacheRootError,
GetVersionFromCachePathError, IsCacheInstalledError, ListCacheVersionsError,
};
#[cfg(not(windows))]
use crate::foundation::get_user_home;
use crate::fs::composite::ensure_dir_exists;
use semver::Version;
use std::path::{Path, PathBuf};
pub trait Cache {
fn version_str(&self) -> String;
fn is_installed(&self) -> Result<bool, IsCacheInstalledError>;
fn delete(&self) -> Result<(), DeleteCacheError>;
fn get_binary_command_path(
&self,
binary_name: &str,
) -> Result<PathBuf, GetBinaryCommandPathError>;
fn get_binary_command(
&self,
binary_name: &str,
) -> Result<std::process::Command, GetBinaryCommandPathError>;
}
pub fn get_cache_root() -> Result<PathBuf, GetCacheRootError> {
let cache_root = std::env::var_os("DFX_CACHE_ROOT");
#[cfg(not(windows))]
let p = {
let home = get_user_home()?;
let root = cache_root.unwrap_or(home);
PathBuf::from(root).join(".cache").join("dfinity")
};
#[cfg(windows)]
let p = match cache_root {
Some(var) => PathBuf::from(var),
None => project_dirs()?.cache_dir().to_owned(),
};
if p.exists() && !p.is_dir() {
return Err(GetCacheRootError::FindCacheDirectoryFailed(p));
}
Ok(p)
}
pub fn get_cache_path_for_version(v: &str) -> Result<PathBuf, GetCacheRootError> {
let p = get_cache_root()?.join("versions").join(v);
Ok(p)
}
pub fn get_version_from_cache_path(
cache_path: &Path,
) -> Result<Version, GetVersionFromCachePathError> {
let version = cache_path
.file_name()
.ok_or(GetVersionFromCachePathError::NoCachePathFilename(
cache_path.to_path_buf(),
))?
.to_str()
.ok_or(GetVersionFromCachePathError::CachePathFilenameNotUtf8(
cache_path.to_path_buf(),
))?;
Ok(Version::parse(version)?)
}
pub fn ensure_cache_versions_dir() -> Result<PathBuf, EnsureCacheVersionsDirError> {
let p = get_cache_root()?.join("versions");
ensure_dir_exists(&p)?;
Ok(p)
}
pub fn get_bin_cache(v: &str) -> Result<PathBuf, EnsureCacheVersionsDirError> {
let root = ensure_cache_versions_dir()?;
Ok(root.join(v))
}
pub fn is_version_installed(v: &str) -> Result<bool, IsCacheInstalledError> {
Ok(get_bin_cache(v)?.is_dir())
}
pub fn delete_version(v: &str) -> Result<bool, DeleteCacheError> {
if !is_version_installed(v).unwrap_or(false) {
return Ok(false);
}
let root = get_bin_cache(v)?;
crate::fs::remove_dir_all(&root)?;
Ok(true)
}
pub fn get_binary_path_from_version(
version: &str,
binary_name: &str,
) -> Result<PathBuf, EnsureCacheVersionsDirError> {
let env_var_name = format!("DFX_{}_PATH", binary_name.replace('-', "_").to_uppercase());
if let Ok(path) = std::env::var(env_var_name) {
return Ok(PathBuf::from(path));
}
Ok(get_bin_cache(version)?.join(binary_name))
}
pub fn binary_command_from_version(
version: &str,
name: &str,
) -> Result<std::process::Command, EnsureCacheVersionsDirError> {
let path = get_binary_path_from_version(version, name)?;
let cmd = std::process::Command::new(path);
Ok(cmd)
}
pub fn list_versions() -> Result<Vec<Version>, ListCacheVersionsError> {
let root = ensure_cache_versions_dir()?;
let mut result: Vec<Version> = Vec::new();
for entry in crate::fs::read_dir(&root)? {
let entry = entry.map_err(ListCacheVersionsError::ReadCacheEntryFailed)?;
if let Some(version) = entry.file_name().to_str() {
if version.starts_with('_') {
continue;
}
result.push(Version::parse(version).map_err(|e| {
ListCacheVersionsError::MalformedSemverString(version.to_string(), e)
})?);
}
}
Ok(result)
}