Skip to main content

xtap_core_lib/
path.rs

1use crate::error::Result;
2use anyhow::Context;
3use std::path::{Path, PathBuf};
4
5/// 获取绝对路径,支持跨平台路径格式化
6pub fn get_absolute_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
7    let path = path.as_ref();
8    std::fs::canonicalize(path).with_context(|| format!("无法解析路径: {:?}", path))
9}
10
11/// 安全地获取用户目录
12pub fn get_home_dir() -> Result<PathBuf> {
13    dirs::home_dir().context("无法获取用户主目录")
14}
15
16/// 检查路径是否存在且是文件
17pub fn is_file_exists<P: AsRef<Path>>(path: P) -> bool {
18    path.as_ref().is_file()
19}
20
21/// 获取系统标准缓存目录下的应用子目录
22///
23/// 跨平台映射:
24/// - macOS:   `~/Library/Caches/<app_name>/`
25/// - Windows: `%LOCALAPPDATA%/<app_name>/cache/`
26/// - Linux:   `~/.cache/<app_name>/`
27///
28/// 目录会被自动创建。返回的 PathBuf 保证存在且可写。
29pub fn cache_dir(app_name: &str) -> Result<PathBuf> {
30    let base = if cfg!(target_os = "macos") {
31        dirs::home_dir()
32            .map(|h| h.join("Library").join("Caches"))
33            .context("无法获取 macOS 缓存目录")
34    } else if cfg!(target_os = "windows") {
35        std::env::var("LOCALAPPDATA")
36            .map(PathBuf::from)
37            .or_else(|_| {
38                dirs::home_dir()
39                    .map(|h| h.join("AppData").join("Local"))
40                    .ok_or(std::env::VarError::NotPresent)
41            })
42            .map(|p| p.join(app_name).join("cache"))
43            .context("无法获取 Windows 缓存目录")
44    } else {
45        // Linux / 其他 Unix
46        std::env::var("XDG_CACHE_HOME")
47            .map(PathBuf::from)
48            .or_else(|_| {
49                dirs::home_dir()
50                    .map(|h| h.join(".cache"))
51                    .ok_or(std::env::VarError::NotPresent)
52            })
53            .context("无法获取缓存目录")
54    }?;
55
56    let dir = if cfg!(target_os = "macos") || cfg!(not(target_os = "windows")) {
57        base.join(app_name)
58    } else {
59        base
60    };
61
62    std::fs::create_dir_all(&dir).with_context(|| format!("创建缓存目录失败: {:?}", dir))?;
63    Ok(dir)
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn test_get_home_dir() {
72        let home = get_home_dir().expect("home dir 应可解析");
73        assert!(home.is_absolute());
74        assert!(home.exists());
75    }
76
77    #[test]
78    fn test_get_absolute_path_preserves_suffix() {
79        // 对当前源码文件做 canonicalize,验证路径拼接结果以目标文件名结尾
80        let me = concat!(env!("CARGO_MANIFEST_DIR"), "/src/path.rs");
81        let abs = get_absolute_path(me).expect("当前文件应存在且可解析");
82        assert!(abs.ends_with("path.rs"));
83    }
84
85    #[test]
86    fn test_is_file_exists() {
87        let me = concat!(env!("CARGO_MANIFEST_DIR"), "/src/path.rs");
88        assert!(is_file_exists(me));
89        assert!(!is_file_exists("/nonexistent/definitely/missing"));
90    }
91}