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
use anyhow::Result;
use serde::Deserialize;
use std::collections::HashMap;
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;

pub fn get_artifact_dir() -> PathBuf {
    let mut artifacts_dir = dirs::home_dir().expect("failed to get home folder");
    artifacts_dir.push(".solc-select");
    artifacts_dir.push("artifacts");
    artifacts_dir
}

fn get_global_version_path() -> PathBuf {
    let mut global_version_path = dirs::home_dir().expect("failed to get home folder");
    global_version_path.push(".solc-select");
    global_version_path.push("global-version");
    global_version_path
}

pub fn install_versions(to_install_versions: Vec<String>) -> Result<()> {
    let all = get_available_versions()?;

    if to_install_versions.is_empty() {
        println!("Available versions to install:");

        for version in all.keys() {
            println!("{}", version);
        }
    } else {
        std::fs::create_dir_all(get_artifact_dir())?;

        for (version, artifact) in &all {
            if to_install_versions.contains(&"all".to_string())
                || to_install_versions.contains(version)
            {
                println!("Installing '{}'...", version);

                let url = format!(
                    "https://binaries.soliditylang.org/{}/{}",
                    soliditylang_platform(),
                    artifact
                );
                let bytes = reqwest::blocking::get(url)?.bytes()?;

                let mut artifact_file = get_artifact_dir();
                artifact_file.push(format!("solc-{}", version));

                std::fs::write(&artifact_file, bytes)
                    .expect("failed to write artifact file to disk.");

                // make it executable
                let mut perms = std::fs::metadata(&artifact_file)
                    .expect("failed to get file metadata")
                    .permissions();
                perms.set_mode(0o775);
                std::fs::set_permissions(&artifact_file, perms)
                    .expect("failed to set to executable");

                println!("Version '{}' installed.", version);
            }
        }
    }

    Ok(())
}

pub fn switch_global_version(version: &str) -> Result<()> {
    if installed_versions()?.contains(&version.to_string()) {
        std::fs::write(get_global_version_path(), version)?;
        println!("Switched global version to {}", version);
    } else if get_available_versions()?
        .keys()
        .any(|v| v.as_str() == version)
    {
        println!(
            "You need to install '{}' prior to using it. Use `solc-select install {}`",
            version, version
        );
    } else {
        println!("Unknown version `{}`", version);
    }

    Ok(())
}

pub fn get_current_version() -> Result<String> {
    Ok(std::fs::read_to_string(get_global_version_path())?
        .trim()
        .to_string())
}

pub fn installed_versions() -> Result<Vec<String>> {
    let artifacts_dir = get_artifact_dir();

    let mut versions = vec![];
    for entry in std::fs::read_dir(artifacts_dir)? {
        let entry = entry?;
        if let Some(version) = entry
            .path()
            .file_name()
            .map(|f| {
                f.to_string_lossy()
                    .strip_prefix("solc-")
                    .map(|s| s.to_string())
            })
            .flatten()
        {
            versions.push(version.to_string());
        }
    }
    Ok(versions)
}

pub fn get_available_versions() -> Result<HashMap<String, String>> {
    let url = format!(
        "https://binaries.soliditylang.org/{}/list.json",
        soliditylang_platform()
    );

    #[derive(Deserialize)]
    struct Response {
        releases: HashMap<String, String>,
    }

    let response: Response = reqwest::blocking::get(&url)?.json()?;

    Ok(response.releases)
}

fn soliditylang_platform() -> &'static str {
    match sys_info::os_type().unwrap_or("".into()).as_str() {
        "Linux" => "linux-amd64",
        "Darwin" => "macosx-amd64",
        _ => panic!("Unsupported platform."),
    }
}