Skip to main content

stackql_mcp/
cache.rs

1//! Cache layout and environment overrides.
2//!
3//! The binary cache is shared with the npm and PyPI wrappers:
4//! `~/.stackql/mcp-server-bin/<version>/<platform-key>/` - always check it
5//! before downloading.
6
7use std::path::PathBuf;
8
9use crate::error::{Error, Result};
10
11/// Path to a stackql binary to run directly, skipping acquisition entirely.
12pub const ENV_BIN: &str = "STACKQL_MCP_BIN";
13/// Path to a local .mcpb bundle to extract instead of downloading.
14pub const ENV_BUNDLE: &str = "STACKQL_MCP_BUNDLE";
15
16/// Resolve the user's home directory without external crates.
17pub fn home_dir() -> Result<PathBuf> {
18    if let Some(home) = std::env::var_os("HOME").filter(|v| !v.is_empty()) {
19        return Ok(PathBuf::from(home));
20    }
21    if cfg!(windows) {
22        if let Some(profile) = std::env::var_os("USERPROFILE").filter(|v| !v.is_empty()) {
23            return Ok(PathBuf::from(profile));
24        }
25    }
26    Err(Error::NoHomeDir)
27}
28
29/// Default approot: `<home>/.stackql`.
30pub fn default_approot() -> Result<PathBuf> {
31    Ok(home_dir()?.join(".stackql"))
32}
33
34/// Root of the shared binary cache: `<home>/.stackql/mcp-server-bin`.
35pub fn bin_cache_root() -> Result<PathBuf> {
36    Ok(default_approot()?.join("mcp-server-bin"))
37}
38
39/// Cache directory for one extracted bundle:
40/// `<home>/.stackql/mcp-server-bin/<version>/<platform-key>/`.
41pub fn bundle_cache_dir(version: &str, platform_key: &str) -> Result<PathBuf> {
42    Ok(bin_cache_root()?.join(version).join(platform_key))
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn cache_dir_matches_the_shared_layout() {
51        let dir = bundle_cache_dir("0.10.500", "linux-x64").unwrap();
52        let suffix: PathBuf = [".stackql", "mcp-server-bin", "0.10.500", "linux-x64"]
53            .iter()
54            .collect();
55        assert!(
56            dir.ends_with(&suffix),
57            "{} should end with {}",
58            dir.display(),
59            suffix.display()
60        );
61    }
62}