dfx_core/config/
cache.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#[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");
    // dirs-next is not used for *nix to preserve existing paths
    #[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)
}

/// Constructs and returns <cache root>/versions/<version> without creating any directories.
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)?)
}

/// Return the binary cache root. It constructs it if not present
/// already.
pub fn ensure_cache_versions_dir() -> Result<PathBuf, EnsureCacheVersionsDirError> {
    let p = get_cache_root()?.join("versions");

    ensure_dir_exists(&p)?;

    Ok(p)
}

/// Doesn't create the version dir, but does create everything up to it
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('_') {
                // temp directory for version being installed
                continue;
            }
            result.push(Version::parse(version).map_err(|e| {
                ListCacheVersionsError::MalformedSemverString(version.to_string(), e)
            })?);
        }
    }

    Ok(result)
}