Skip to main content

xtask_todo_lib/devshell/sandbox/
paths.rs

1//! Export base directory resolution and `PATH` lookup.
2
3use std::path::PathBuf;
4
5/// Override parent directory for sandbox exports (`devshell_*` folders). Trims whitespace; empty ignores.
6pub const ENV_EXPORT_BASE: &str = "DEVSHELL_EXPORT_BASE";
7
8/// Parent directory for per-run `devshell_*` export folders.
9///
10/// Many Linux systems mount [`std::env::temp_dir`] (often `/tmp`) with **`noexec`**, so `cargo run`
11/// can build but fails to **execute** `target/debug/...` with **Permission denied (EACCES)**. This
12/// defaults to a user cache path that is normally executable.
13///
14/// Resolution order:
15/// 1. **`DEVSHELL_EXPORT_BASE`** if set and non-empty.
16/// 2. Unix: **`XDG_CACHE_HOME`/cargo-devshell-exports**, else **`HOME`/.cache/cargo-devshell-exports**.
17/// 3. Windows: **`LOCALAPPDATA`/cargo-devshell-exports**.
18/// 4. Fall back to [`std::env::temp_dir`].
19#[must_use]
20pub fn devshell_export_parent_dir() -> PathBuf {
21    if let Ok(p) = std::env::var(ENV_EXPORT_BASE) {
22        let p = p.trim();
23        if !p.is_empty() {
24            return PathBuf::from(p);
25        }
26    }
27    #[cfg(unix)]
28    {
29        if let Ok(xdg) = std::env::var("XDG_CACHE_HOME") {
30            let xdg = xdg.trim();
31            if !xdg.is_empty() {
32                return PathBuf::from(xdg).join("cargo-devshell-exports");
33            }
34        }
35        if let Ok(home) = std::env::var("HOME") {
36            let home = home.trim();
37            if !home.is_empty() {
38                return PathBuf::from(home)
39                    .join(".cache")
40                    .join("cargo-devshell-exports");
41            }
42        }
43    }
44    #[cfg(windows)]
45    {
46        if let Ok(local) = std::env::var("LOCALAPPDATA") {
47            let local = local.trim();
48            if !local.is_empty() {
49                return PathBuf::from(local).join("cargo-devshell-exports");
50            }
51        }
52    }
53    std::env::temp_dir()
54}
55
56/// Search for `program` in PATH. Returns the first absolute path where the executable exists.
57#[must_use]
58pub fn find_in_path(program: &str) -> Option<PathBuf> {
59    let path_env = std::env::var_os("PATH")?;
60    let ext = if cfg!(windows) { ".exe" } else { "" };
61    for part in std::env::split_paths(&path_env) {
62        let candidate = part.join(format!("{program}{ext}"));
63        if candidate.is_file() {
64            return Some(candidate);
65        }
66    }
67    None
68}