refactor: improved error handling for base bin discovery process

This commit is contained in:
Erica Marigold 2024-03-11 19:11:57 +05:30
parent 52e71850d1
commit 93b950ce10
No known key found for this signature in database
GPG key ID: 2768CC0C23D245D1

View file

@ -4,12 +4,17 @@ use std::{
process::ExitCode, process::ExitCode,
}; };
use anyhow::{Context, Result}; use anyhow::{bail, Context, Error, Result};
use async_compression::tokio::bufread::DeflateDecoder;
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 tokio::{fs, io::AsyncWriteExt as _}; use thiserror::Error;
use tokio::{
fs,
io::{AsyncReadExt, AsyncWriteExt as _},
};
use crate::standalone::metadata::Metadata; use crate::standalone::metadata::Metadata;
@ -41,6 +46,9 @@ pub struct BuildCommand {
pub base: Option<PathBuf>, pub base: Option<PathBuf>,
} }
// TODO: Currently, the file we are patching is user provided, so we should
// probably check whether the binary is a valid lune base binary first
impl BuildCommand { impl BuildCommand {
pub async fn run(self) -> Result<ExitCode> { pub async fn run(self) -> Result<ExitCode> {
let mut output_path = self let mut output_path = self
@ -55,7 +63,24 @@ impl BuildCommand {
.context("failed to read input file")?; .context("failed to read input file")?;
// Dynamically derive the base executable path based on the CLI arguments provided // Dynamically derive the base executable path based on the CLI arguments provided
let base_exe_path = get_base_exe_path(self.base, self.target, &mut output_path).await; let base_exe_path = match get_base_exe_path(self.base, self.target, &mut output_path).await
{
Ok(path) => Some(path),
Err(err) => {
let inner_err = err.downcast::<BasePathDiscoveryError<()>>();
if let Err(other_err) = inner_err {
bail!(
"Encountered an error while handling cross-compilation flags: {}",
other_err
);
}
// If there the downcasted error was ok, it is safe to continue since
// neither the --base nor the --target flags were set
None
}
};
// Read the contents of the lune interpreter as our starting point // Read the contents of the lune interpreter as our starting point
println!( println!(
@ -94,27 +119,39 @@ async fn write_executable_file_to(path: impl AsRef<Path>, bytes: impl AsRef<[u8]
Ok(()) Ok(())
} }
#[derive(Debug, Clone, Error, PartialEq)]
pub enum BasePathDiscoveryError<T> {
#[error("decompression error")]
Decompression(T),
#[error("precompiled base for target not found for {target}")]
TargetNotFound { target: String },
#[error("failed to download precompiled binary base")]
DownloadError(T),
#[error("a generic error related to an io operation occurred")]
IoError(T),
#[error("neither a custom base path or precompiled target name provided")]
None,
}
async fn get_base_exe_path( async fn get_base_exe_path(
base: Option<PathBuf>, base: Option<PathBuf>,
target: Option<String>, target: Option<String>,
output_path: &mut PathBuf, output_path: &mut PathBuf,
) -> Option<PathBuf> { ) -> Result<PathBuf> {
if base.is_some() { if let Some(base) = base {
output_path.set_extension( output_path.set_extension(
base.clone() base.extension()
.unwrap()
.extension()
.expect("failed to get extension of base binary"), .expect("failed to get extension of base binary"),
); );
base Ok(base)
} else if let Some(target_inner) = target { } else if let Some(target_inner) = target {
let target_exe_extension = match target_inner.as_str() { let target_exe_extension = match target_inner.as_str() {
"windows-x86_64" => "exe", "windows-x86_64" => "exe",
_ => "bin", _ => "bin",
}; };
let path = TARGET_BASE_DIR.join(format!("lune-{}.{}", target_inner, target_exe_extension)); let path = TARGET_BASE_DIR.join(format!("lune-{target_inner}.{target_exe_extension}"));
output_path.set_extension(if target_exe_extension == "bin" { output_path.set_extension(if target_exe_extension == "bin" {
"" ""
@ -125,7 +162,7 @@ async fn get_base_exe_path(
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
.ok()?; .map_err(BasePathDiscoveryError::IoError)?;
} }
if !path.exists() { if !path.exists() {
@ -150,17 +187,16 @@ async fn get_base_exe_path(
); );
// Maybe we should use the custom net client used in `@lune/net` // Maybe we should use the custom net client used in `@lune/net`
let dl_req = match reqwest::get(release_url).await { let resp = reqwest::get(release_url).await.map_err(|err| {
Err(_) => {
eprintln!( eprintln!(
" {} Unable to download base binary found for target `{}`", " {} Unable to download base binary found for target `{}`",
style("Download").red().bold(), style("Download").red().bold(),
target_inner, target_inner,
); );
return None; BasePathDiscoveryError::DownloadError::<Error>(err.into())
} })?;
Ok(resp) => {
let resp_status = resp.status(); let resp_status = resp.status();
if resp_status != 200 && !resp_status.is_redirection() { if resp_status != 200 && !resp_status.is_redirection() {
@ -172,12 +208,23 @@ async fn get_base_exe_path(
println!("{}: {}", style("HINT").yellow(), style("Perhaps try providing a path to self-compiled target with the `--base` flag").italic()); println!("{}: {}", style("HINT").yellow(), style("Perhaps try providing a path to self-compiled target with the `--base` flag").italic());
return None; return Err(BasePathDiscoveryError::TargetNotFound::<String> {
target: target_inner,
}
.into());
} }
resp let compressed_reader = resp
} .bytes()
}; .await
.map_err(BasePathDiscoveryError::IoError)?;
let mut decompressed_bytes = vec![];
// This errors, so idk what decoder to use
DeflateDecoder::new(compressed_reader.as_ref())
.read_to_end(&mut decompressed_bytes)
.await
.map_err(BasePathDiscoveryError::Decompression)?;
fs::OpenOptions::new() fs::OpenOptions::new()
.write(true) .write(true)
@ -185,10 +232,10 @@ async fn get_base_exe_path(
.truncate(true) .truncate(true)
.open(&path) .open(&path)
.await .await
.ok()? .map_err(BasePathDiscoveryError::IoError)?
.write_all(&dl_req.bytes().await.ok()?) .write_all(&decompressed_bytes)
.await .await
.ok()?; .map_err(BasePathDiscoveryError::IoError)?;
println!( println!(
" {} {}", " {} {}",
@ -197,8 +244,8 @@ async fn get_base_exe_path(
); );
} }
Some(path) Ok(path)
} else { } else {
None Err(BasePathDiscoveryError::<()>::None.into())
} }
} }