use std::path::PathBuf;
use console::style;
use tokio::task::JoinHandle;
use super::spinner::Spinner;
use crate::cargo::{BuildError, BuildOpts};
#[derive(Clone, Copy)]
pub enum BuildKind {
Initial,
Rebuild,
}
impl BuildKind {
fn label(self) -> &'static str {
match self {
Self::Initial => "building",
Self::Rebuild => "rebuilding",
}
}
}
pub struct BuildTask {
handle: JoinHandle<Option<PathBuf>>,
}
impl BuildTask {
pub fn spawn(kind: BuildKind, opts: BuildOpts) -> Self {
Self {
handle: tokio::spawn(build(kind, opts)),
}
}
pub async fn finished(&mut self) -> Option<PathBuf> {
(&mut self.handle).await.expect("build task panicked")
}
pub async fn cancel(mut self) {
self.handle.abort();
let _ = (&mut self.handle).await;
}
}
impl Drop for BuildTask {
fn drop(&mut self) {
self.handle.abort();
}
}
async fn build(kind: BuildKind, opts: BuildOpts) -> Option<PathBuf> {
let label = kind.label();
let spinner = Spinner::new(label);
let progress = spinner.bar();
let result = crate::cargo::build_and_read(&opts, move |current, total| {
progress.set_message(format!("{label} ({current}/{total})"));
})
.await;
drop(spinner);
let (exe, bytes) = match result {
Ok(built) => built,
Err(error) => {
report_build_error(&error);
return None;
}
};
let spinner = Spinner::new("bundling assets");
let bundled = crate::asset::run_bundle(&bytes, None).await;
drop(spinner);
if let Err(error) = bundled {
eprintln!(
" {}",
style(format!("failed to bundle assets: {error}"))
.red()
.bold()
);
eprintln!();
return None;
}
Some(exe)
}
fn report_build_error(error: &BuildError) {
eprintln!(" {}", style("build failed").red().bold());
eprintln!();
if let BuildError::Failed { rendered } = error {
eprint!("{rendered}");
} else {
eprintln!(" {}", style(error.to_string()).red());
}
eprintln!();
}