Skip to main content

nash_cli/
proxy.rs

1use std::path::PathBuf;
2
3use miette::{IntoDiagnostic, Result};
4
5/// Walk up from cwd looking for `nash.jsonc`. If the project requires a different
6/// compiler version, exec the correct binary (downloading it first if needed).
7pub async fn maybe_proxy() -> Result<()> {
8    let cwd = std::env::current_dir().into_diagnostic()?;
9
10    let config_path = match find_config(&cwd) {
11        Some(p) => p,
12        None => return Ok(()),
13    };
14
15    let config = nash_config::parse_file(&config_path).into_diagnostic()?;
16
17    let required = match config.compiler() {
18        Some(v) if v != crate::VERSION => v,
19        _ => return Ok(()),
20    };
21
22    let cache_dir = cached_binary_dir(required);
23    let binary = cached_binary_path(required);
24
25    if binary.exists() {
26        exec_cached_binary(&binary);
27    }
28
29    crate::download::download_version(required, &cache_dir).await?;
30    exec_cached_binary(&binary);
31}
32
33/// Search for `nash.jsonc` by walking up from `start`.
34fn find_config(start: &std::path::Path) -> Option<PathBuf> {
35    let mut dir = start.to_path_buf();
36    loop {
37        let candidate = dir.join(nash_config::CONFIG_FILE_NAME);
38        if candidate.exists() {
39            return Some(candidate);
40        }
41        if !dir.pop() {
42            return None;
43        }
44    }
45}
46
47fn cached_binary_dir(version: &str) -> PathBuf {
48    let base = std::env::var("NASH_HOME")
49        .map(PathBuf::from)
50        .unwrap_or_else(|_| {
51            dirs::data_dir()
52                .unwrap_or_else(|| PathBuf::from("."))
53                .join("nash")
54        });
55    base.join("versions").join(version)
56}
57
58fn cached_binary_path(version: &str) -> PathBuf {
59    let dir = cached_binary_dir(version);
60    if cfg!(windows) {
61        dir.join("nash.exe")
62    } else {
63        dir.join("nash")
64    }
65}
66
67/// Replace this process with the cached binary, forwarding all args.
68fn exec_cached_binary(binary: &std::path::Path) -> ! {
69    let args: Vec<String> = std::env::args().skip(1).collect();
70
71    #[cfg(unix)]
72    {
73        use std::os::unix::process::CommandExt;
74        let err = std::process::Command::new(binary).args(&args).exec();
75        eprintln!("Failed to exec cached nash binary: {err}");
76        std::process::exit(1);
77    }
78
79    #[cfg(not(unix))]
80    {
81        let status = std::process::Command::new(binary)
82            .args(&args)
83            .status()
84            .unwrap_or_else(|e| {
85                eprintln!("Failed to run cached nash binary: {e}");
86                std::process::exit(1);
87            });
88        std::process::exit(status.code().unwrap_or(1));
89    }
90}