refactor: remove panic-prone unwrap and expect calls

This commit is contained in:
lbl
2026-08-23 12:04:40 +08:00
parent 2100ec1ca5
commit c2fd1ff870
18 changed files with 161 additions and 128 deletions
+33 -21
View File
@@ -5,16 +5,20 @@
//! - 找不到 pnpm 时:已有产物则告警并沿用;没有产物则报错并给出指引
//! - 设置环境变量 VNT_WEB_SKIP_UI_BUILD=1 可完全跳过前端构建
use std::error::Error;
use std::io;
use std::path::Path;
use std::process::Command;
use std::time::SystemTime;
fn main() {
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR");
fn main() -> Result<(), Box<dyn Error>> {
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?;
let manifest_dir = Path::new(&manifest_dir);
let ui_dir = manifest_dir.join("ui");
let static_dir = manifest_dir.join("static");
let workspace_root = manifest_dir.parent().expect("workspace root");
let workspace_root = manifest_dir
.parent()
.ok_or_else(|| io::Error::other("vnt-web manifest directory has no parent"))?;
// UI 源码变化时重新运行本脚本
println!("cargo:rerun-if-changed={}", ui_dir.join("src").display());
@@ -38,12 +42,12 @@ fn main() {
println!("cargo:rerun-if-env-changed=VNT_WEB_SKIP_UI_BUILD");
if std::env::var("VNT_WEB_SKIP_UI_BUILD").is_ok() {
ensure_static_placeholder(&static_dir);
return;
ensure_static_placeholder(&static_dir)?;
return Ok(());
}
if static_is_fresh(&ui_dir, &static_dir) {
return;
return Ok(());
}
let Some(pnpm) = find_pnpm() else {
@@ -51,20 +55,22 @@ fn main() {
println!(
"cargo:warning=未找到 pnpm,沿用 vnt-web/static 中已有的前端产物(可能不是最新)"
);
return;
return Ok(());
}
panic!(
return Err(io::Error::other(
"未找到 pnpm 且 vnt-web/static 没有前端产物。\n\
请安装 Node.js 与 pnpm 后重新构建(cargo 会自动完成前端构建),\n\
或从发布包中获取 static 目录放入 vnt-web/。"
);
或从发布包中获取 static 目录放入 vnt-web/。",
)
.into());
};
if !ui_dir.join("node_modules").is_dir() {
// ui 依赖使用 workspace catalog,必须在仓库根目录安装
run_or_panic(pnpm, &["install", "--frozen-lockfile"], workspace_root);
run_command(pnpm, &["install", "--frozen-lockfile"], workspace_root)?;
}
run_or_panic(pnpm, &["--filter", "vnt-web-ui", "build"], workspace_root);
run_command(pnpm, &["--filter", "vnt-web-ui", "build"], workspace_root)?;
Ok(())
}
/// pnpm 命令名(Windows 上是 pnpm.cmd,由 cmd.exe 执行)
@@ -80,7 +86,7 @@ fn find_pnpm() -> Option<&'static str> {
.find(|cmd| Command::new(cmd).arg("--version").output().is_ok())
}
fn run_or_panic(program: &str, args: &[&str], dir: &Path) {
fn run_command(program: &str, args: &[&str], dir: &Path) -> io::Result<()> {
println!(
"cargo:warning=执行前端构建: {} {} ({})",
program,
@@ -91,15 +97,21 @@ fn run_or_panic(program: &str, args: &[&str], dir: &Path) {
.args(args)
.current_dir(dir)
.status()
.unwrap_or_else(|e| panic!("执行 {} 失败: {}", program, e));
.map_err(|error| {
io::Error::new(
error.kind(),
format!("执行 {program} 失败(目录 {}):{error}", dir.display()),
)
})?;
if !status.success() {
panic!(
return Err(io::Error::other(format!(
"前端构建失败: {} {} (exit: {:?})",
program,
args.join(" "),
status.code()
);
)));
}
Ok(())
}
/// static 产物是否比 UI 源码新
@@ -128,16 +140,16 @@ fn newest_mtime(dir: &Path) -> Option<SystemTime> {
}
/// 跳过构建时保证 static/ 存在,使 rust_embed 可以编译
fn ensure_static_placeholder(static_dir: &Path) {
fn ensure_static_placeholder(static_dir: &Path) -> io::Result<()> {
if static_dir.join("index.html").is_file() {
return;
return Ok(());
}
println!("cargo:warning=VNT_WEB_SKIP_UI_BUILD 已设置且 static 为空,写入占位页面");
std::fs::create_dir_all(static_dir).expect("创建 static 目录失败");
std::fs::create_dir_all(static_dir)?;
std::fs::write(
static_dir.join("index.html"),
"<!doctype html><html><body><p>VNT Web UI 未构建。请安装 pnpm 后重新执行 cargo build\
或取消 VNT_WEB_SKIP_UI_BUILD。</p></body></html>",
)
.expect("写入占位页面失败");
)?;
Ok(())
}