use crate::error::Error;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Platform {
pub os: String,
pub cpu: String,
pub libc: Option<String>,
}
impl Platform {
pub fn current() -> Result<Platform, Error> {
let os = match std::env::consts::OS {
"macos" => "darwin",
"linux" => "linux",
"windows" => "win32",
other => {
return Err(Error::UnsupportedPlatform {
platform: format!("{other}-{}", std::env::consts::ARCH),
});
}
};
let cpu = match std::env::consts::ARCH {
"x86_64" => "x64",
"aarch64" => "arm64",
"x86" => "x86",
"powerpc64" => "ppc64",
"s390x" => "s390x",
other => other,
};
let libc = (os == "linux" && detect_musl()).then(|| "musl".to_string());
Ok(Platform {
os: os.to_string(),
cpu: cpu.to_string(),
libc,
})
}
pub fn dist_slug(&self) -> String {
let os = if self.os == "win32" { "win" } else { &self.os };
let musl = if self.libc.as_deref() == Some("musl") {
"-musl"
} else {
""
};
format!("{os}-{}{musl}", self.cpu)
}
pub fn index_files_token(&self) -> String {
match self.os.as_str() {
"darwin" => format!("osx-{}-tar", self.cpu),
"win32" => format!("win-{}-zip", self.cpu),
_ => format!("linux-{}", self.cpu),
}
}
pub fn archive_ext(&self) -> &'static str {
if self.os == "win32" { "zip" } else { "tar.gz" }
}
pub fn archive_kind(&self) -> &'static str {
if self.os == "win32" { "zip" } else { "tarball" }
}
pub fn label(&self) -> String {
match &self.libc {
Some(libc) => format!("{}-{} ({libc})", self.os, self.cpu),
None => format!("{}-{}", self.os, self.cpu),
}
}
}
#[cfg(target_os = "linux")]
fn detect_musl() -> bool {
std::path::Path::new(&format!("/lib/ld-musl-{}.so.1", std::env::consts::ARCH)).exists()
}
#[cfg(not(target_os = "linux"))]
fn detect_musl() -> bool {
false
}
pub fn artifact_filename(version: &node_semver::Version, platform: &Platform) -> String {
format!(
"node-v{version}-{}.{}",
platform.dist_slug(),
platform.archive_ext()
)
}
pub fn artifact_top_dir(version: &node_semver::Version, platform: &Platform) -> String {
format!("node-v{version}-{}", platform.dist_slug())
}
#[cfg(test)]
mod tests {
use super::*;
fn plat(os: &str, cpu: &str, libc: Option<&str>) -> Platform {
Platform {
os: os.into(),
cpu: cpu.into(),
libc: libc.map(String::from),
}
}
#[test]
fn dist_slugs() {
assert_eq!(plat("darwin", "arm64", None).dist_slug(), "darwin-arm64");
assert_eq!(plat("linux", "x64", None).dist_slug(), "linux-x64");
assert_eq!(
plat("linux", "x64", Some("musl")).dist_slug(),
"linux-x64-musl"
);
assert_eq!(plat("win32", "x64", None).dist_slug(), "win-x64");
}
#[test]
fn index_tokens() {
assert_eq!(
plat("darwin", "arm64", None).index_files_token(),
"osx-arm64-tar"
);
assert_eq!(
plat("win32", "x64", None).index_files_token(),
"win-x64-zip"
);
assert_eq!(
plat("linux", "arm64", None).index_files_token(),
"linux-arm64"
);
assert_eq!(
plat("linux", "x64", Some("musl")).index_files_token(),
"linux-x64"
);
}
#[test]
fn artifact_names() {
let v: node_semver::Version = "22.1.0".parse().unwrap();
assert_eq!(
artifact_filename(&v, &plat("win32", "x64", None)),
"node-v22.1.0-win-x64.zip"
);
assert_eq!(
artifact_top_dir(&v, &plat("darwin", "arm64", None)),
"node-v22.1.0-darwin-arm64"
);
}
}