solc_select/
lib.rs

1use anyhow::Result;
2use serde::Deserialize;
3use std::collections::HashMap;
4use std::io::Read;
5use std::os::unix::fs::PermissionsExt;
6use std::path::PathBuf;
7
8pub fn get_artifact_dir() -> PathBuf {
9    let mut artifacts_dir = dirs::home_dir().expect("failed to get home folder");
10    artifacts_dir.push(".solc-select");
11    artifacts_dir.push("artifacts");
12    artifacts_dir
13}
14
15fn get_global_version_path() -> PathBuf {
16    let mut global_version_path = dirs::home_dir().expect("failed to get home folder");
17    global_version_path.push(".solc-select");
18    global_version_path.push("global-version");
19    global_version_path
20}
21
22pub fn install_versions(to_install_versions: Vec<String>) -> Result<()> {
23    let all = get_available_versions()?;
24
25    if to_install_versions.is_empty() {
26        println!("Available versions to install:");
27
28        // let's use the reverse order to print out
29        let mut versions: Vec<_> = all.keys().cloned().collect();
30        versions.sort_by(|a, b| {
31            /// returns the (major, minor, patch)
32            fn split_version(s: &str) -> (u32, u32, u32) {
33                let parts: Vec<_> = s.split(".").collect();
34                let mut major = 0;
35                let mut minor = 0;
36                let mut patch = 0;
37                if parts.len() >= 1 {
38                    major = parts[0]
39                        .parse::<u32>()
40                        .expect("failed to parse solidity version");
41                }
42                if parts.len() >= 2 {
43                    minor = parts[1]
44                        .parse::<u32>()
45                        .expect("failed to parse solidity version");
46                }
47                if parts.len() >= 3 {
48                    patch = parts[2]
49                        .parse::<u32>()
50                        .expect("failed to parse solidity version");
51                }
52                (major, minor, patch)
53            }
54
55            split_version(b).cmp(&split_version(a))
56        });
57
58        for version in versions {
59            println!("{}", version);
60        }
61    } else {
62        std::fs::create_dir_all(get_artifact_dir())?;
63
64        for (version, artifact) in &all {
65            if to_install_versions.contains(&"all".to_string())
66                || to_install_versions.contains(version)
67            {
68                println!("Installing '{}'...", version);
69
70                let url = format!(
71                    "https://binaries.soliditylang.org/{}/{}",
72                    soliditylang_platform(),
73                    artifact
74                );
75                let mut bytes: Vec<u8> = Vec::new();
76                let response = ureq::get(&url).call()?;
77                response.into_reader().read_to_end(&mut bytes)?;
78
79                let mut artifact_file = get_artifact_dir();
80                artifact_file.push(format!("solc-{}", version));
81
82                std::fs::write(&artifact_file, bytes)
83                    .expect("failed to write artifact file to disk.");
84
85                // make it executable
86                let mut perms = std::fs::metadata(&artifact_file)
87                    .expect("failed to get file metadata")
88                    .permissions();
89                perms.set_mode(0o775);
90                std::fs::set_permissions(&artifact_file, perms)
91                    .expect("failed to set to executable");
92
93                println!("Version '{}' installed.", version);
94            }
95        }
96    }
97
98    Ok(())
99}
100
101pub fn switch_global_version(version: &str) -> Result<()> {
102    if installed_versions()?.contains(&version.to_string()) {
103        std::fs::write(get_global_version_path(), version)?;
104        println!("Switched global version to {}", version);
105    } else if get_available_versions()?
106        .keys()
107        .any(|v| v.as_str() == version)
108    {
109        println!(
110            "You need to install '{}' prior to using it. Use `solc-select install {}`",
111            version, version
112        );
113    } else {
114        println!("Unknown version `{}`", version);
115    }
116
117    Ok(())
118}
119
120pub fn get_current_version() -> Result<String> {
121    Ok(std::fs::read_to_string(get_global_version_path())?
122        .trim()
123        .to_string())
124}
125
126pub fn installed_versions() -> Result<Vec<String>> {
127    let artifacts_dir = get_artifact_dir();
128
129    let mut versions = vec![];
130    for entry in std::fs::read_dir(artifacts_dir)? {
131        let entry = entry?;
132        if let Some(version) = entry
133            .path()
134            .file_name()
135            .map(|f| {
136                f.to_string_lossy()
137                    .strip_prefix("solc-")
138                    .map(|s| s.to_string())
139            })
140            .flatten()
141        {
142            versions.push(version.to_string());
143        }
144    }
145    Ok(versions)
146}
147
148pub fn get_available_versions() -> Result<HashMap<String, String>> {
149    let url = format!(
150        "https://binaries.soliditylang.org/{}/list.json",
151        soliditylang_platform()
152    );
153
154    #[derive(Deserialize)]
155    struct Response {
156        releases: HashMap<String, String>,
157    }
158
159    let response: Response = ureq::get(&url).call()?.into_json()?;
160
161    Ok(response.releases)
162}
163
164fn soliditylang_platform() -> &'static str {
165    match sys_info::os_type().unwrap_or("".into()).as_str() {
166        "Linux" => "linux-amd64",
167        "Darwin" => "macosx-amd64",
168        _ => panic!("Unsupported platform."),
169    }
170}