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 mut versions: Vec<_> = all.keys().cloned().collect();
30 versions.sort_by(|a, b| {
31 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 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}