rust_webx_core/paths.rs
1//! Application base directory resolution.
2//!
3//! 解析应用基准目录(`app_base`):appsettings.json 所在目录。
4//! 框架的配置加载、SPA 静态资源定位均基于此目录,确保部署后从部署目录
5//! 而非源码目录读取资源。
6//!
7//! 优先级:
8//! 0. **环境变量 `RUST_WEBX_APP_BASE`**:显式指定基准目录(测试 / 容器挂载场景)。
9//! 1. **exe 同级目录**:若 exe 所在目录存在 `appsettings.json`,视为部署目录
10//! (`cargo build --release` 后由发布脚本产出的布局,从任意 cwd 启动均生效)。
11//! 2. **cwd 同级目录**:若当前工作目录存在 `appsettings.json`,视为基准目录
12//! (从部署目录启动 exe 的常见场景)。
13//! 3. **cwd 向上遍历**:在 cwd 的各祖先的子目录中查找含 `appsettings.json`
14//! 的目录(`cargo run` 从 workspace 根启动时定位 member crate 目录)。
15//! 4. **回退 cwd**:以上均未命中时返回当前工作目录,由调用方按相对路径处理。
16
17use std::path::{Path, PathBuf};
18
19/// 判断给定目录是否为「应用基准目录」(存在 `appsettings.json`)。
20///
21/// `appsettings.json` 是框架配套配置文件的标志,存在即视为基准目录。
22/// 不依赖 `wwwroot/`,因为并非所有应用都启用 SPA。
23pub fn looks_like_app_base(dir: &Path) -> bool {
24 dir.join("appsettings.json").exists()
25}
26
27/// 解析应用基准目录。优先级见模块文档。
28pub fn app_base() -> PathBuf {
29 // 0. 显式环境变量(测试 / 自定义部署路径)
30 if let Ok(base) = std::env::var("RUST_WEBX_APP_BASE") {
31 let path = PathBuf::from(&base);
32 if path.is_dir() {
33 return path;
34 }
35 }
36
37 // 1. exe 同级目录(部署场景)
38 if let Ok(exe) = std::env::current_exe() {
39 if let Some(exe_dir) = exe.parent() {
40 if looks_like_app_base(exe_dir) {
41 return exe_dir.to_path_buf();
42 }
43 }
44 }
45
46 // 2. cwd 同级目录
47 if let Ok(cwd) = std::env::current_dir() {
48 if looks_like_app_base(&cwd) {
49 return cwd;
50 }
51
52 // 3. cwd 向上遍历,在各祖先的子目录中查找
53 let mut dir: Option<&Path> = Some(cwd.as_path());
54 while let Some(d) = dir {
55 if let Ok(entries) = std::fs::read_dir(d) {
56 for entry in entries.flatten() {
57 let p = entry.path();
58 if p.is_dir() && looks_like_app_base(&p) {
59 return p;
60 }
61 }
62 }
63 dir = d.parent();
64 }
65 }
66
67 // 4. 回退 cwd
68 std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
69}