use std::path::{Path, PathBuf};
use crate::{
build::{BuildProgress, RustBuild, RustLinkage},
project::Project,
templates::{self, TemplateContext},
water_dir,
};
async fn launcher_dir(project: &Project) -> eyre::Result<PathBuf> {
Ok(water_dir::project_build_cache_dir(project.root())
.await?
.join("tui"))
}
async fn template_context(project: &Project, dir: &Path) -> eyre::Result<TemplateContext> {
let manifest = project.manifest();
let app_name = manifest
.package
.name
.chars()
.filter(|c| c.is_alphanumeric())
.collect::<String>();
Ok(TemplateContext::for_project_manifest(
manifest,
project.crate_name().clone(),
app_name,
&project.resolved_framework().await?,
)
.with_backend_project_path(dir.to_path_buf())
.with_project_root_path(project.root().to_path_buf()))
}
async fn requires_regeneration(project: &Project, dir: &Path) -> eyre::Result<bool> {
let ctx = template_context(project, dir).await?;
for (relative, expected) in
templates::tui::rendered_outputs(&ctx, project.tui_backend_crate_name().as_str())?
{
match std::fs::read(dir.join(&relative)) {
Ok(existing) if existing == expected => {}
Ok(_) | Err(_) => return Ok(true),
}
}
Ok(false)
}
pub async fn ensure_launcher(project: &Project) -> eyre::Result<PathBuf> {
let dir = launcher_dir(project).await?;
if requires_regeneration(project, &dir).await? {
let ctx = template_context(project, &dir).await?;
templates::tui::scaffold(&dir, &ctx, project.tui_backend_crate_name().as_str()).await?;
}
Ok(dir)
}
pub async fn build(
project: &Project,
launcher_dir: &Path,
sccache_path: Option<PathBuf>,
progress: Option<BuildProgress>,
) -> eyre::Result<PathBuf> {
let mut build = RustBuild::new(launcher_dir, target_lexicon::Triple::host())
.with_project(project)
.with_target_dir(project.water_target_dir(RustLinkage::Static).await?);
if let Some(sccache_path) = sccache_path {
build = build.with_sccache(sccache_path);
}
if let Some(progress) = progress {
build = build.with_progress(progress);
}
build
.build_binary(project.tui_backend_crate_name().as_str(), false)
.await
.map_err(|error| eyre::eyre!("failed to build the TUI launcher: {error}"))
}
pub fn exec(binary: &Path) -> eyre::Result<()> {
use std::io::Write as _;
let _ = std::io::stdout().flush();
#[cfg(unix)]
{
use eyre::WrapErr as _;
use std::os::unix::process::CommandExt as _;
Err(std::process::Command::new(binary).exec())
.wrap_err_with(|| format!("failed to launch the TUI binary {}", binary.display()))
}
#[cfg(not(unix))]
{
let status = std::process::Command::new(binary).status()?;
if !status.success() {
eyre::bail!("the TUI application exited with {status}");
}
Ok(())
}
}