forge_core_utils/
lib.rs

1use std::{env, sync::OnceLock};
2
3use directories::ProjectDirs;
4
5pub mod approvals;
6pub mod assets;
7pub mod browser;
8pub mod diff;
9pub mod git;
10pub mod log_msg;
11pub mod msg_store;
12pub mod path;
13pub mod port_file;
14pub mod response;
15pub mod sentry;
16pub mod shell;
17pub mod stream_ext;
18pub mod stream_lines;
19pub mod text;
20pub mod tokio;
21pub mod version;
22
23/// Cache for WSL2 detection result
24static WSL2_CACHE: OnceLock<bool> = OnceLock::new();
25
26/// Check if running in WSL2 (cached)
27pub fn is_wsl2() -> bool {
28    *WSL2_CACHE.get_or_init(|| {
29        // Check for WSL environment variables
30        if std::env::var("WSL_DISTRO_NAME").is_ok() || std::env::var("WSLENV").is_ok() {
31            tracing::debug!("WSL2 detected via environment variables");
32            return true;
33        }
34
35        // Check /proc/version for WSL2 signature
36        if let Ok(version) = std::fs::read_to_string("/proc/version")
37            && (version.contains("WSL2") || version.contains("microsoft"))
38        {
39            tracing::debug!("WSL2 detected via /proc/version");
40            return true;
41        }
42
43        tracing::debug!("WSL2 not detected");
44        false
45    })
46}
47
48pub fn cache_dir() -> std::path::PathBuf {
49    let proj = if cfg!(debug_assertions) {
50        ProjectDirs::from("ai", "namastex-dev", env!("CARGO_PKG_NAME"))
51            .expect("OS didn't give us a home directory")
52    } else {
53        ProjectDirs::from("ai", "namastex", env!("CARGO_PKG_NAME"))
54            .expect("OS didn't give us a home directory")
55    };
56
57    // ✔ macOS → ~/Library/Caches/MyApp
58    // ✔ Linux → ~/.cache/myapp (respects XDG_CACHE_HOME)
59    // ✔ Windows → %LOCALAPPDATA%\Example\MyApp
60    proj.cache_dir().to_path_buf()
61}
62
63// Get or create cached PowerShell script file
64pub async fn get_powershell_script()
65-> Result<std::path::PathBuf, Box<dyn std::error::Error + Send + Sync>> {
66    use std::io::Write;
67
68    let cache_dir = cache_dir();
69    let script_path = cache_dir.join("toast-notification.ps1");
70
71    // Check if cached file already exists and is valid
72    if script_path.exists() {
73        // Verify file has content (basic validation)
74        if let Ok(metadata) = std::fs::metadata(&script_path)
75            && metadata.len() > 0
76        {
77            return Ok(script_path);
78        }
79    }
80
81    // File doesn't exist or is invalid, create it
82    let script_content = assets::ScriptAssets::get("toast-notification.ps1")
83        .ok_or("Embedded PowerShell script not found: toast-notification.ps1")?
84        .data;
85
86    // Ensure cache directory exists
87    std::fs::create_dir_all(&cache_dir)
88        .map_err(|e| format!("Failed to create cache directory: {e}"))?;
89
90    let mut file = std::fs::File::create(&script_path)
91        .map_err(|e| format!("Failed to create PowerShell script file: {e}"))?;
92
93    file.write_all(&script_content)
94        .map_err(|e| format!("Failed to write PowerShell script data: {e}"))?;
95
96    drop(file); // Ensure file is closed
97
98    Ok(script_path)
99}