Refactor downloading lune binary to cache, some fixes + formatting

This commit is contained in:
Filip Tibell 2024-04-20 17:19:08 +02:00
parent 53463641b8
commit 0685e62a8f
No known key found for this signature in database
3 changed files with 311 additions and 131 deletions

251
Cargo.lock generated
View file

@ -17,6 +17,17 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aes"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.1.3" version = "1.1.3"
@ -110,6 +121,15 @@ version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
[[package]]
name = "arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
dependencies = [
"derive_arbitrary",
]
[[package]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.7" version = "0.3.7"
@ -150,7 +170,6 @@ dependencies = [
"brotli", "brotli",
"flate2", "flate2",
"futures-core", "futures-core",
"futures-io",
"memchr", "memchr",
"pin-project-lite", "pin-project-lite",
"tokio", "tokio",
@ -197,21 +216,6 @@ dependencies = [
"syn 2.0.59", "syn 2.0.59",
] ]
[[package]]
name = "async_zip"
version = "0.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527207465fb6dcafbf661b0d4a51d0d2306c9d0c2975423079a6caa807930daf"
dependencies = [
"async-compression",
"crc32fast",
"futures-lite",
"pin-project",
"thiserror",
"tokio",
"tokio-util",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
@ -374,11 +378,36 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]]
name = "bzip2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
dependencies = [
"bzip2-sys",
"libc",
]
[[package]]
name = "bzip2-sys"
version = "0.1.11+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.94" version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7"
dependencies = [
"jobserver",
"libc",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -421,6 +450,16 @@ dependencies = [
"walkdir", "walkdir",
] ]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.4" version = "4.5.4"
@ -470,6 +509,15 @@ dependencies = [
"error-code", "error-code",
] ]
[[package]]
name = "cmake"
version = "0.1.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.0" version = "1.0.0"
@ -557,6 +605,21 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crc"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.4.0" version = "1.4.0"
@ -588,6 +651,12 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]]
name = "deflate64"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83ace6c86376be0b6cdcf3fb41882e81d94b31587573d1cfa9d01cd06bba210d"
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.3.11" version = "0.3.11"
@ -597,6 +666,17 @@ dependencies = [
"powerfmt", "powerfmt",
] ]
[[package]]
name = "derive_arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.59",
]
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "0.99.17" version = "0.99.17"
@ -631,6 +711,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [ dependencies = [
"block-buffer", "block-buffer",
"crypto-common", "crypto-common",
"subtle",
] ]
[[package]] [[package]]
@ -824,6 +905,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"libz-ng-sys",
"miniz_oxide", "miniz_oxide",
] ]
@ -1020,6 +1102,15 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]] [[package]]
name = "home" name = "home"
version = "0.5.9" version = "0.5.9"
@ -1260,6 +1351,15 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "ipnet" name = "ipnet"
version = "2.9.0" version = "2.9.0"
@ -1281,6 +1381,15 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jobserver"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "685a7d121ee3f65ae4fddd72b25a04bb36b6af81bc0828f7d5434c0fe60fa3a2"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.69" version = "0.3.69"
@ -1322,6 +1431,16 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "libz-ng-sys"
version = "1.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6409efc61b12687963e602df8ecf70e8ddacf95bc6576bcf16e3ac6328083c5"
dependencies = [
"cmake",
"libc",
]
[[package]] [[package]]
name = "line-wrap" name = "line-wrap"
version = "0.2.0" version = "0.2.0"
@ -1366,7 +1485,6 @@ dependencies = [
"anyhow", "anyhow",
"async-compression", "async-compression",
"async-trait", "async-trait",
"async_zip",
"blocking", "blocking",
"bstr", "bstr",
"chrono", "chrono",
@ -1410,11 +1528,11 @@ dependencies = [
"thiserror", "thiserror",
"tokio", "tokio",
"tokio-tungstenite", "tokio-tungstenite",
"tokio-util",
"toml", "toml",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"urlencoding", "urlencoding",
"zip_next",
] ]
[[package]] [[package]]
@ -1446,6 +1564,16 @@ dependencies = [
"twox-hash", "twox-hash",
] ]
[[package]]
name = "lzma-rs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
dependencies = [
"byteorder 1.5.0",
"crc",
]
[[package]] [[package]]
name = "matchers" name = "matchers"
version = "0.1.0" version = "0.1.0"
@ -1691,6 +1819,16 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]]
name = "pbkdf2"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
dependencies = [
"digest",
"hmac",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.1" version = "2.3.1"
@ -2432,6 +2570,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@ -2790,7 +2934,6 @@ checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
"futures-io",
"futures-sink", "futures-sink",
"pin-project-lite", "pin-project-lite",
"tokio", "tokio",
@ -2959,6 +3102,12 @@ dependencies = [
"static_assertions", "static_assertions",
] ]
[[package]]
name = "typed-arena"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.17.0" version = "1.17.0"
@ -3391,3 +3540,67 @@ name = "zeroize"
version = "1.7.0" version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
[[package]]
name = "zip_next"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9519d1479ea50c3b79f1e00eacbb58e311c72c721e08313ebe64d8617a31b5d1"
dependencies = [
"aes",
"arbitrary",
"byteorder 1.5.0",
"bzip2",
"constant_time_eq 0.3.0",
"crc32fast",
"crossbeam-utils",
"deflate64",
"flate2",
"hmac",
"lzma-rs",
"pbkdf2",
"sha1 0.10.6",
"time 0.3.36",
"zopfli",
"zstd",
]
[[package]]
name = "zopfli"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c1f48f3508a3a3f2faee01629564400bc12260f6214a056d06a3aaaa6ef0736"
dependencies = [
"crc32fast",
"log",
"simd-adler32",
"typed-arena",
]
[[package]]
name = "zstd"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.10+zstd.1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa"
dependencies = [
"cc",
"pkg-config",
]

View file

@ -26,8 +26,7 @@ cli = [
"dep:include_dir", "dep:include_dir",
"dep:regex", "dep:regex",
"dep:rustyline", "dep:rustyline",
"dep:async_zip", "dep:zip_next",
"dep:tokio-util",
] ]
roblox = [ roblox = [
"dep:glam", "dep:glam",
@ -139,6 +138,7 @@ regex = { optional = true, version = "1.7", default-features = false, features =
"unicode-perl", "unicode-perl",
] } ] }
rustyline = { optional = true, version = "14.0" } rustyline = { optional = true, version = "14.0" }
zip_next = { optional = true, version = "1.1" }
### ROBLOX ### ROBLOX
@ -152,10 +152,3 @@ rbx_dom_weak = { optional = true, version = "2.6.0" }
rbx_reflection = { optional = true, version = "4.4.0" } rbx_reflection = { optional = true, version = "4.4.0" }
rbx_reflection_database = { optional = true, version = "0.2.9" } rbx_reflection_database = { optional = true, version = "0.2.9" }
rbx_xml = { optional = true, version = "0.13.2" } rbx_xml = { optional = true, version = "0.13.2" }
### CROSS COMPILATION
async_zip = { optional = true, version = "0.0.16", features = [
"tokio",
"deflate",
] }
tokio-util = { optional = true, version = "0.7", features = ["io-util"] }

View file

@ -1,22 +1,17 @@
use std::{ use std::{
env::consts, env::consts,
io::Cursor, io::{Cursor, Read},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::ExitCode, process::ExitCode,
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use async_zip::base::read::seek::ZipFileReader;
use clap::Parser; use clap::Parser;
use console::style; use console::style;
use directories::BaseDirs; use directories::BaseDirs;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use thiserror::Error; use thiserror::Error;
use tokio::{ use tokio::{fs, io::AsyncWriteExt, task::spawn_blocking};
fs,
io::{AsyncReadExt, AsyncWriteExt},
};
use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt};
use crate::standalone::metadata::{Metadata, CURRENT_EXE}; use crate::standalone::metadata::{Metadata, CURRENT_EXE};
@ -30,7 +25,7 @@ const TARGET_BASE_DIR: Lazy<PathBuf> = Lazy::new(|| {
.join(env!("CARGO_PKG_VERSION")) .join(env!("CARGO_PKG_VERSION"))
}); });
// Build a standalone executable /// Build a standalone executable
#[derive(Debug, Clone, Parser)] #[derive(Debug, Clone, Parser)]
pub struct BuildCommand { pub struct BuildCommand {
/// The path to the input file /// The path to the input file
@ -64,9 +59,8 @@ impl BuildCommand {
// Read the contents of the lune interpreter as our starting point // Read the contents of the lune interpreter as our starting point
println!( println!(
"{} standalone binary using {}", "Compiling standalone binary from {}",
style("Compile").green().bold(), style(input_path_displayed).green()
style(input_path_displayed).underlined()
); );
let patched_bin = Metadata::create_env_patched_bin(base_exe_path, source_code.clone()) let patched_bin = Metadata::create_env_patched_bin(base_exe_path, source_code.clone())
.await .await
@ -74,9 +68,8 @@ impl BuildCommand {
// And finally write the patched binary to the output file // And finally write the patched binary to the output file
println!( println!(
" {} standalone binary to {}", "Writing standalone binary to {}",
style("Write").blue().bold(), style(output_path.display()).blue()
style(output_path.display()).underlined()
); );
write_executable_file_to(output_path, patched_bin).await?; // Read & execute for all, write for owner write_executable_file_to(output_path, patched_bin).await?; // Read & execute for all, write for owner
@ -84,7 +77,10 @@ impl BuildCommand {
} }
} }
async fn write_executable_file_to(path: impl AsRef<Path>, bytes: impl AsRef<[u8]>) -> Result<()> { async fn write_executable_file_to(
path: impl AsRef<Path>,
bytes: impl AsRef<[u8]>,
) -> Result<(), std::io::Error> {
let mut options = fs::OpenOptions::new(); let mut options = fs::OpenOptions::new();
options.write(true).create(true).truncate(true); options.write(true).create(true).truncate(true);
@ -99,27 +95,30 @@ async fn write_executable_file_to(path: impl AsRef<Path>, bytes: impl AsRef<[u8]
Ok(()) Ok(())
} }
/// Possible ways in which the discovery and/or download of a base binary's path can error /// Errors that may occur when building a standalone binary
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum BasePathDiscoveryError { pub enum BuildError {
/// An error in the decompression of the precompiled target #[error("failed to find lune target '{0}' in GitHub release")]
#[error("decompression error")] ReleaseTargetNotFound(String),
Decompression(#[from] async_zip::error::ZipError), #[error("failed to find lune binary '{0}' in downloaded zip file")]
#[error("precompiled base for target not found for {target}")] ZippedBinaryNotFound(String),
TargetNotFound { target: String }, #[error("failed to download lune binary: {0}")]
/// An error in the precompiled target download process Download(#[from] reqwest::Error),
#[error("failed to download precompiled binary base, reason: {0}")] #[error("failed to unzip lune binary: {0}")]
DownloadError(#[from] reqwest::Error), Unzip(#[from] zip_next::result::ZipError),
/// An IO related error #[error("panicked while unzipping lune binary: {0}")]
#[error("a generic error related to an io operation occurred, details: {0}")] UnzipJoin(#[from] tokio::task::JoinError),
IoError(#[from] anyhow::Error), #[error("io error: {0}")]
IoError(#[from] std::io::Error),
} }
pub type BuildResult<T, E = BuildError> = std::result::Result<T, E>;
/// Discovers the path to the base executable to use for cross-compilation /// Discovers the path to the base executable to use for cross-compilation
async fn get_base_exe_path( async fn get_base_exe_path(
target: Option<String>, target: Option<String>,
output_path: PathBuf, output_path: PathBuf,
) -> Result<(PathBuf, PathBuf), BasePathDiscoveryError> { ) -> BuildResult<(PathBuf, PathBuf)> {
if let Some(target_inner) = target { if let Some(target_inner) = target {
let current_target = format!("{}-{}", consts::OS, consts::ARCH); let current_target = format!("{}-{}", consts::OS, consts::ARCH);
@ -140,16 +139,13 @@ async fn get_base_exe_path(
// Create the target base directory in the lune home if it doesn't already exist // Create the target base directory in the lune home if it doesn't already exist
if !TARGET_BASE_DIR.exists() { if !TARGET_BASE_DIR.exists() {
fs::create_dir_all(TARGET_BASE_DIR.to_path_buf()) fs::create_dir_all(TARGET_BASE_DIR.to_path_buf()).await?;
.await
.map_err(anyhow::Error::from)
.map_err(BasePathDiscoveryError::IoError)?;
} }
// If a cached target base executable doesn't exist, attempt to download it // If a cached target base executable doesn't exist, attempt to download it
if !path.exists() { if !path.exists() {
println!("Requested target hasn't been downloaded yet, attempting to download"); println!("Requested target does not exist in cache and must be downloaded");
cache_target(target_inner, target_exe_extension, &path).await?; download_target_to_cache(target_inner, target_exe_extension, &path).await?;
} }
Ok((path, output_path.with_extension(target_exe_extension))) Ok((path, output_path.with_extension(target_exe_extension)))
@ -162,89 +158,67 @@ async fn get_base_exe_path(
} }
} }
async fn cache_target( /// Downloads the target base executable to the cache directory
async fn download_target_to_cache(
target: String, target: String,
target_exe_extension: &str, target_exe_extension: &str,
path: &PathBuf, path: &PathBuf,
) -> Result<(), BasePathDiscoveryError> { ) -> BuildResult<()> {
let version = env!("CARGO_PKG_VERSION");
let target_triple = format!("lune-{version}-{target}");
let release_url = format!( let release_url = format!(
"https://github.com/lune-org/lune/releases/download/v{ver}/lune-{ver}-{target}.zip", "{base_url}/v{version}/{target_triple}.zip",
ver = env!("CARGO_PKG_VERSION"), base_url = "https://github.com/lune-org/lune/releases/download",
target = target
); );
println!("Downloading {target_triple}");
let target_full_display = release_url // Try to request to download the zip file from the target url,
.split('/') // making sure transient errors are handled gracefully and
.last() // with a different error message than "not found"
.unwrap_or("lune-UNKNOWN-UNKNOWN") let response = reqwest::get(release_url).await?;
.replace(".zip", format!(".{target_exe_extension}").as_str()); if !response.status().is_success() {
if response.status().as_u16() == 404 {
println!( return Err(BuildError::ReleaseTargetNotFound(target));
"{} target {}", }
style("Download").green().bold(), return Err(BuildError::Download(
target_full_display response.error_for_status().unwrap_err(),
); ));
let resp = reqwest::get(release_url).await.map_err(|err| {
eprintln!(
" {} Unable to download base binary found for target `{}`",
style("Download").red().bold(),
target,
);
BasePathDiscoveryError::DownloadError(err)
})?;
let resp_status = resp.status();
if resp_status != 200 && !resp_status.is_redirection() {
eprintln!(
" {} No precompiled base binary found for target `{}`",
style("Download").red().bold(),
target
);
return Err(BasePathDiscoveryError::TargetNotFound { target });
} }
// Wrap the request response in bytes so that we can decompress it, since `async_zip` // Receive the full zip file
// requires the underlying reader to implement `AsyncRead` and `Seek`, which `Bytes` let zip_bytes = response.bytes().await?.to_vec();
// doesn't implement let zip_file = Cursor::new(zip_bytes);
let compressed_data = Cursor::new(
resp.bytes() // Look for and extract the binary file from the zip file
.await let binary_file_name = format!(
.map_err(anyhow::Error::from) "lune{}{target_exe_extension}",
.map_err(BasePathDiscoveryError::IoError)? if target_exe_extension.is_empty() {
.to_vec(), ""
} else {
"."
}
); );
// Construct a decoder and decompress the ZIP file using deflate // NOTE: We use spawn_blocking here since reading a
let mut decoder = ZipFileReader::new(compressed_data.compat()) // zip archive is a somewhat slow / blocking operation
.await let binary_file_handle = spawn_blocking(move || {
.map_err(BasePathDiscoveryError::Decompression)?; let mut archive = zip_next::ZipArchive::new(zip_file)?;
let mut decompressed = vec![]; let mut binary = Vec::new();
archive
.by_name(&binary_file_name)
.or(Err(BuildError::ZippedBinaryNotFound(binary_file_name)))?
.read_to_end(&mut binary)?;
decoder Ok::<_, BuildError>(binary)
.reader_without_entry(0) });
.await let binary_file_contents = binary_file_handle.await??;
.map_err(BasePathDiscoveryError::Decompression)?
.compat()
.read_to_end(&mut decompressed)
.await
.map_err(anyhow::Error::from)
.map_err(BasePathDiscoveryError::IoError)?;
// Finally write the decompressed data to the target base directory // Finally write the decompressed data to the target base directory
write_executable_file_to(&path, decompressed) write_executable_file_to(&path, binary_file_contents).await?;
.await
.map_err(BasePathDiscoveryError::IoError)?;
println!( println!("Downloaded {target_triple} successfully");
" {} {}",
style("Downloaded").blue(),
style(target_full_display).underlined()
);
Ok(()) Ok(())
} }