xtap-core-lib 0.5.1

星TAP实验室通用 Rust 基座:跨平台路径/IO/硬件/MCP HTTP 服务/egui 主题与字体(features 按需裁剪)
Documentation
use crate::error::Result;
use anyhow::Context;
use std::path::{Path, PathBuf};

/// 获取绝对路径,支持跨平台路径格式化
pub fn get_absolute_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
    let path = path.as_ref();
    std::fs::canonicalize(path).with_context(|| format!("无法解析路径: {:?}", path))
}

/// 安全地获取用户目录
pub fn get_home_dir() -> Result<PathBuf> {
    dirs::home_dir().context("无法获取用户主目录")
}

/// 检查路径是否存在且是文件
pub fn is_file_exists<P: AsRef<Path>>(path: P) -> bool {
    path.as_ref().is_file()
}

/// 获取系统标准缓存目录下的应用子目录
///
/// 跨平台映射:
/// - macOS:   `~/Library/Caches/<app_name>/`
/// - Windows: `%LOCALAPPDATA%/<app_name>/cache/`
/// - Linux:   `~/.cache/<app_name>/`
///
/// 目录会被自动创建。返回的 PathBuf 保证存在且可写。
pub fn cache_dir(app_name: &str) -> Result<PathBuf> {
    let base = if cfg!(target_os = "macos") {
        dirs::home_dir()
            .map(|h| h.join("Library").join("Caches"))
            .context("无法获取 macOS 缓存目录")
    } else if cfg!(target_os = "windows") {
        std::env::var("LOCALAPPDATA")
            .map(PathBuf::from)
            .or_else(|_| {
                dirs::home_dir()
                    .map(|h| h.join("AppData").join("Local"))
                    .ok_or(std::env::VarError::NotPresent)
            })
            .map(|p| p.join(app_name).join("cache"))
            .context("无法获取 Windows 缓存目录")
    } else {
        // Linux / 其他 Unix
        std::env::var("XDG_CACHE_HOME")
            .map(PathBuf::from)
            .or_else(|_| {
                dirs::home_dir()
                    .map(|h| h.join(".cache"))
                    .ok_or(std::env::VarError::NotPresent)
            })
            .context("无法获取缓存目录")
    }?;

    let dir = if cfg!(target_os = "macos") || cfg!(not(target_os = "windows")) {
        base.join(app_name)
    } else {
        base
    };

    std::fs::create_dir_all(&dir).with_context(|| format!("创建缓存目录失败: {:?}", dir))?;
    Ok(dir)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_home_dir() {
        let home = get_home_dir().expect("home dir 应可解析");
        assert!(home.is_absolute());
        assert!(home.exists());
    }

    #[test]
    fn test_get_absolute_path_preserves_suffix() {
        // 对当前源码文件做 canonicalize,验证路径拼接结果以目标文件名结尾
        let me = concat!(env!("CARGO_MANIFEST_DIR"), "/src/path.rs");
        let abs = get_absolute_path(me).expect("当前文件应存在且可解析");
        assert!(abs.ends_with("path.rs"));
    }

    #[test]
    fn test_is_file_exists() {
        let me = concat!(env!("CARGO_MANIFEST_DIR"), "/src/path.rs");
        assert!(is_file_exists(me));
        assert!(!is_file_exists("/nonexistent/definitely/missing"));
    }
}