1#![doc = include_str!("../README.md")]
2#![doc(
3 html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg",
4 html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico"
5)]
6#![warn(rustdoc::all)]
7#![cfg_attr(
8 not(any(test, feature = "cli", feature = "solc")),
9 warn(unused_crate_dependencies)
10)]
11#![deny(unused_must_use, rust_2018_idioms)]
12#![cfg_attr(docsrs, feature(doc_cfg))]
13
14use semver::Version;
15use std::fs;
16
17mod error;
18pub use error::SvmError;
19
20mod install;
21#[cfg(feature = "blocking")]
22pub use install::blocking_install;
23pub use install::install;
24
25mod paths;
26pub use paths::{data_dir, global_version_path, setup_data_dir, version_binary, version_path};
27
28mod platform;
29pub use platform::{Platform, platform};
30
31mod releases;
32pub use releases::{BuildInfo, Releases, all_releases};
33
34#[cfg(feature = "blocking")]
35pub use releases::blocking_all_releases;
36
37#[cfg(feature = "cli")]
38#[doc(hidden)]
39pub const VERSION_MESSAGE: &str = concat!(
40 env!("CARGO_PKG_VERSION"),
41 " (",
42 env!("VERGEN_GIT_SHA"),
43 " ",
44 env!("VERGEN_BUILD_DATE"),
45 ")"
46);
47
48pub fn get_global_version() -> Result<Option<Version>, SvmError> {
50 let v = fs::read_to_string(global_version_path())?;
51 Ok(Version::parse(v.trim_end_matches('\n')).ok())
52}
53
54pub fn set_global_version(version: &Version) -> Result<(), SvmError> {
56 fs::write(global_version_path(), version.to_string()).map_err(Into::into)
57}
58
59pub fn unset_global_version() -> Result<(), SvmError> {
61 fs::write(global_version_path(), "").map_err(Into::into)
62}
63
64pub fn installed_versions() -> Result<Vec<Version>, SvmError> {
67 let mut versions = vec![];
68 for v in fs::read_dir(data_dir())? {
69 let path = v?.path();
70 if !path.is_dir() {
74 continue;
75 }
76 let Some(file_name) = path.file_name().and_then(|file_name| file_name.to_str()) else {
77 continue;
78 };
79 let Ok(version) = Version::parse(file_name) else {
80 continue;
81 };
82 if !version_binary(file_name).is_file() {
85 continue;
86 }
87 versions.push(version);
88 }
89 versions.sort();
90 Ok(versions)
91}
92
93#[cfg(feature = "blocking")]
95pub fn blocking_all_versions() -> Result<Vec<Version>, SvmError> {
96 Ok(releases::blocking_all_releases(platform::platform())?.into_versions())
97}
98
99pub async fn all_versions() -> Result<Vec<Version>, SvmError> {
102 Ok(releases::all_releases(platform::platform())
103 .await?
104 .into_versions())
105}
106
107pub fn remove_version(version: &Version) -> Result<(), SvmError> {
113 fs::remove_dir_all(version_path(version.to_string().as_str())).map_err(Into::into)
114}
115
116fn setup_version(version: &str) -> Result<(), SvmError> {
117 let v = version_path(version);
118 if !v.exists() {
119 fs::create_dir_all(v)?;
120 }
121 Ok(())
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
131 fn installed_versions_ignores_auxiliary_entries() {
132 setup_data_dir().unwrap();
133 let dir = data_dir();
134 fs::write(dir.join(".lock-solc-0.8.10"), "").unwrap();
135 fs::write(dir.join(".tmpXYZ123"), "").unwrap();
136 fs::write(dir.join(".DS_Store"), "").unwrap();
137 for version in ["0.8.10", "0.8.24"] {
138 fs::create_dir_all(version_path(version)).unwrap();
139 fs::write(version_binary(version), "solc").unwrap();
140 }
141 fs::create_dir_all(version_path("99.99.99")).unwrap();
143
144 let versions = installed_versions().unwrap();
145 assert!(versions.contains(&Version::new(0, 8, 10)));
146 assert!(versions.contains(&Version::new(0, 8, 24)));
147 assert!(!versions.contains(&Version::new(99, 99, 99)));
148 }
149}