include!("driver_urls.rs");
#[allow(dead_code)]
fn assemble_driver(
driver_dir: &std::path::Path,
driver_version: &str,
platform: &str,
) -> std::io::Result<()> {
use std::io;
let triple = node_triple(platform).ok_or_else(|| {
io::Error::other(format!("unsupported driver platform: {platform}"))
})?;
let parent = driver_dir
.parent()
.ok_or_else(|| io::Error::other("driver dir has no parent"))?;
std::fs::create_dir_all(parent)?;
let tmp = parent.join(format!(
".{}.partial-{}",
driver_dir
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("driver"),
std::process::id()
));
let _ = std::fs::remove_dir_all(&tmp);
std::fs::create_dir_all(&tmp)?;
let result = (|| {
extract_npm_package(&http_get(&npm_core_url(driver_version))?, &tmp)?;
extract_node_binary(
&http_get(&node_archive_url(NODE_VERSION, triple))?,
NODE_VERSION,
triple,
&tmp,
)?;
let node_name = if is_windows_platform(platform) {
"node.exe"
} else {
"node"
};
for required in [tmp.join(node_name), tmp.join("package").join("cli.js")] {
if !required.exists() {
return Err(io::Error::other(format!(
"assembled driver is missing {}",
required.display()
)));
}
}
Ok(())
})();
if let Err(e) = result {
let _ = std::fs::remove_dir_all(&tmp);
return Err(e);
}
if driver_dir.exists() {
let _ = std::fs::remove_dir_all(&tmp);
return Ok(());
}
std::fs::rename(&tmp, driver_dir)
}
#[allow(dead_code)]
fn http_get(url: &str) -> std::io::Result<Vec<u8>> {
use std::io;
let mut response = ureq::get(url)
.call()
.map_err(|e| io::Error::other(format!("download of {url} failed: {e}")))?;
let status = response.status().as_u16();
if !(200..300).contains(&status) {
return Err(io::Error::other(format!(
"download of {url} failed with status {status}"
)));
}
response
.body_mut()
.with_config()
.limit(u64::MAX)
.read_to_vec()
.map_err(|e| io::Error::other(format!("reading {url} failed: {e}")))
}
#[allow(dead_code)]
fn extract_npm_package(tgz: &[u8], dest: &std::path::Path) -> std::io::Result<()> {
use std::io;
let gz = flate2::read::GzDecoder::new(tgz);
let mut archive = tar::Archive::new(gz);
let mut entries = 0usize;
for entry in archive.entries()? {
let mut entry = entry?;
if entry.path()?.starts_with("package") {
entry.unpack_in(dest)?;
entries += 1;
}
}
if entries == 0 {
return Err(io::Error::other(
"npm tarball contained no package/ entries",
));
}
Ok(())
}
#[allow(dead_code)]
fn extract_node_binary(
archive_bytes: &[u8],
node_version: &str,
triple: &str,
dest: &std::path::Path,
) -> std::io::Result<()> {
use std::io;
use std::io::Read;
let exe_inner = node_archive_exe_path(node_version, triple);
let license_inner = format!("node-v{node_version}-{triple}/LICENSE");
let mut found_exe = false;
if triple.starts_with("win-") {
let cursor = io::Cursor::new(archive_bytes.to_vec());
let mut archive = zip::ZipArchive::new(cursor)
.map_err(|e| io::Error::other(format!("Node archive is not a valid zip: {e}")))?;
for i in 0..archive.len() {
let mut file = archive
.by_index(i)
.map_err(|e| io::Error::other(format!("zip read failed: {e}")))?;
let name = file.name().to_string();
let out = if name == exe_inner {
found_exe = true;
dest.join("node.exe")
} else if name == license_inner {
dest.join("LICENSE")
} else {
continue;
};
let mut bytes = Vec::new();
file.read_to_end(&mut bytes)?;
std::fs::write(&out, bytes)?;
}
} else {
let gz = flate2::read::GzDecoder::new(archive_bytes);
let mut archive = tar::Archive::new(gz);
for entry in archive.entries()? {
let mut entry = entry?;
let path = entry.path()?.to_path_buf();
let out = if path == std::path::Path::new(&exe_inner) {
found_exe = true;
dest.join("node")
} else if path == std::path::Path::new(&license_inner) {
dest.join("LICENSE")
} else {
continue;
};
entry.unpack(&out)?;
#[cfg(unix)]
if out.ends_with("node") {
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(&out)?.permissions();
perms.set_mode(0o755);
std::fs::set_permissions(&out, perms)?;
}
}
}
if !found_exe {
return Err(io::Error::other(format!(
"Node archive did not contain {exe_inner}"
)));
}
Ok(())
}