1use crate::error::Error;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Platform {
14 pub os: String,
16 pub cpu: String,
19 pub libc: Option<String>,
21}
22
23impl Platform {
24 pub fn current() -> Result<Platform, Error> {
32 let os = match std::env::consts::OS {
33 "macos" => "darwin",
34 "linux" => "linux",
35 "windows" => "win32",
36 other => {
37 return Err(Error::UnsupportedPlatform {
38 platform: format!("{other}-{}", std::env::consts::ARCH),
39 });
40 }
41 };
42 let cpu = match std::env::consts::ARCH {
43 "x86_64" => "x64",
44 "aarch64" => "arm64",
45 "x86" => "x86",
46 "powerpc64" => "ppc64",
47 "s390x" => "s390x",
48 other => other,
49 };
50 let libc = (os == "linux" && detect_musl()).then(|| "musl".to_string());
51 Ok(Platform {
52 os: os.to_string(),
53 cpu: cpu.to_string(),
54 libc,
55 })
56 }
57
58 pub fn dist_slug(&self) -> String {
61 let os = if self.os == "win32" { "win" } else { &self.os };
62 let musl = if self.libc.as_deref() == Some("musl") {
63 "-musl"
64 } else {
65 ""
66 };
67 format!("{os}-{}{musl}", self.cpu)
68 }
69
70 pub fn index_files_token(&self) -> String {
79 match self.os.as_str() {
80 "darwin" => format!("osx-{}-tar", self.cpu),
81 "win32" => format!("win-{}-zip", self.cpu),
82 _ => format!("linux-{}", self.cpu),
83 }
84 }
85
86 pub fn archive_ext(&self) -> &'static str {
88 if self.os == "win32" { "zip" } else { "tar.gz" }
89 }
90
91 pub fn archive_kind(&self) -> &'static str {
93 if self.os == "win32" { "zip" } else { "tarball" }
94 }
95
96 pub fn label(&self) -> String {
98 match &self.libc {
99 Some(libc) => format!("{}-{} ({libc})", self.os, self.cpu),
100 None => format!("{}-{}", self.os, self.cpu),
101 }
102 }
103}
104
105#[cfg(target_os = "linux")]
106fn detect_musl() -> bool {
107 std::path::Path::new(&format!("/lib/ld-musl-{}.so.1", std::env::consts::ARCH)).exists()
111}
112
113#[cfg(not(target_os = "linux"))]
114fn detect_musl() -> bool {
115 false
116}
117
118pub fn artifact_filename(version: &node_semver::Version, platform: &Platform) -> String {
121 format!(
122 "node-v{version}-{}.{}",
123 platform.dist_slug(),
124 platform.archive_ext()
125 )
126}
127
128pub fn artifact_top_dir(version: &node_semver::Version, platform: &Platform) -> String {
131 format!("node-v{version}-{}", platform.dist_slug())
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 fn plat(os: &str, cpu: &str, libc: Option<&str>) -> Platform {
139 Platform {
140 os: os.into(),
141 cpu: cpu.into(),
142 libc: libc.map(String::from),
143 }
144 }
145
146 #[test]
147 fn dist_slugs() {
148 assert_eq!(plat("darwin", "arm64", None).dist_slug(), "darwin-arm64");
149 assert_eq!(plat("linux", "x64", None).dist_slug(), "linux-x64");
150 assert_eq!(
151 plat("linux", "x64", Some("musl")).dist_slug(),
152 "linux-x64-musl"
153 );
154 assert_eq!(plat("win32", "x64", None).dist_slug(), "win-x64");
155 }
156
157 #[test]
158 fn index_tokens() {
159 assert_eq!(
160 plat("darwin", "arm64", None).index_files_token(),
161 "osx-arm64-tar"
162 );
163 assert_eq!(
164 plat("win32", "x64", None).index_files_token(),
165 "win-x64-zip"
166 );
167 assert_eq!(
168 plat("linux", "arm64", None).index_files_token(),
169 "linux-arm64"
170 );
171 assert_eq!(
172 plat("linux", "x64", Some("musl")).index_files_token(),
173 "linux-x64"
174 );
175 }
176
177 #[test]
178 fn artifact_names() {
179 let v: node_semver::Version = "22.1.0".parse().unwrap();
180 assert_eq!(
181 artifact_filename(&v, &plat("win32", "x64", None)),
182 "node-v22.1.0-win-x64.zip"
183 );
184 assert_eq!(
185 artifact_top_dir(&v, &plat("darwin", "arm64", None)),
186 "node-v22.1.0-darwin-arm64"
187 );
188 }
189}