use std::path::{Path, PathBuf};
use std::process::Command;
fn main() {
let crate_root = crate_root();
let ui_dir = crate_root.join("ui");
let ui_dist = ui_dir.join("dist");
emit_rerun_directives();
if skip_requested() {
handle_skip(&ui_dist);
return;
}
if !has_ui_sources(&ui_dir) {
ensure_placeholder(&ui_dist);
return;
}
build_ui(&ui_dir, &ui_dist);
}
fn crate_root() -> PathBuf {
PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap_or_default())
}
fn emit_rerun_directives() {
println!("cargo:rerun-if-env-changed=SKIP_UI_BUILD");
println!("cargo:rerun-if-changed=ui/package.json");
println!("cargo:rerun-if-changed=ui/vite.config.js");
println!("cargo:rerun-if-changed=ui/index.html");
println!("cargo:rerun-if-changed=ui/src");
}
fn skip_requested() -> bool {
std::env::var("SKIP_UI_BUILD").as_deref() == Ok("1")
}
fn handle_skip(ui_dist: &Path) {
if !ui_dist.join("index.html").exists() {
println!(
"cargo:warning=SKIP_UI_BUILD=1 but ui/dist/ is empty. \
Run `pnpm -C ui build` before publishing."
);
ensure_placeholder(ui_dist);
}
}
fn has_ui_sources(ui_dir: &Path) -> bool {
ui_dir.join("package.json").exists()
}
fn package_manager(_ui_dir: &Path) -> Option<&'static str> {
let has = |bin: &str| {
Command::new(bin)
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
};
if has("pnpm") {
Some("pnpm")
} else if has("npm") {
Some("npm")
} else {
None
}
}
fn build_ui(ui_dir: &Path, ui_dist: &Path) {
let Some(pm) = package_manager(ui_dir) else {
println!(
"cargo:warning=no pnpm/npm found; cannot build trusty-memory UI. \
Run `pnpm -C ui build` manually or set SKIP_UI_BUILD=1."
);
ensure_placeholder(ui_dist);
return;
};
let install = Command::new(pm).arg("install").current_dir(ui_dir).status();
if !matches!(install, Ok(s) if s.success()) {
println!("cargo:warning=`{pm} install` failed for trusty-memory UI");
ensure_placeholder(ui_dist);
return;
}
let build = Command::new(pm)
.args(["run", "build"])
.current_dir(ui_dir)
.status();
match build {
Ok(s) if s.success() => {}
Ok(s) => {
println!("cargo:warning=`{pm} run build` exited with {s:?}");
ensure_placeholder(ui_dist);
}
Err(e) => {
println!("cargo:warning=failed to spawn `{pm} run build` ({e})");
ensure_placeholder(ui_dist);
}
}
}
fn ensure_placeholder(ui_dist: &Path) {
if ui_dist.join("index.html").exists() {
return;
}
let _ = std::fs::create_dir_all(ui_dist);
let _ = std::fs::write(
ui_dist.join("index.html"),
"<!doctype html><html><body><p>trusty-memory: UI assets not built. \
Run <code>pnpm -C ui build</code> and rebuild.</p></body></html>",
);
}