Skip to main content

candle_graph/
analysis_cache.rs

1//! Optional on-disk cache for analyzed [`crate::model_ir::ModelIr`] documents.
2//!
3//! Enabled when `CANDLE_GRAPH_CACHE=1` or `--cache` is passed. Cache files live under
4//! `CANDLE_GRAPH_CACHE_DIR` (default: `$XDG_CACHE_HOME/candle-graph` or `/tmp/candle-graph-cache`).
5
6use std::path::{Path, PathBuf};
7
8use anyhow::{Context, Result};
9
10use crate::model_ir::ModelIr;
11
12pub fn cache_enabled(explicit: bool) -> bool {
13    explicit
14        || std::env::var("CANDLE_GRAPH_CACHE")
15            .map(|value| matches!(value.as_str(), "1" | "true" | "yes"))
16            .unwrap_or(false)
17}
18
19pub fn cache_dir() -> PathBuf {
20    std::env::var("CANDLE_GRAPH_CACHE_DIR")
21        .map(PathBuf::from)
22        .or_else(|_| {
23            std::env::var("XDG_CACHE_HOME")
24                .map(|home| PathBuf::from(home).join("candle-graph"))
25                .or_else(|_| {
26                    std::env::var("HOME")
27                        .map(|home| PathBuf::from(home).join(".cache/candle-graph"))
28                })
29        })
30        .unwrap_or_else(|_| PathBuf::from("/tmp/candle-graph-cache"))
31}
32
33pub fn cache_path(analysis_id: &str) -> PathBuf {
34    let safe = analysis_id
35        .chars()
36        .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
37        .collect::<String>();
38    cache_dir().join(format!("{safe}.json"))
39}
40
41pub fn load(path: &Path) -> Result<Option<ModelIr>> {
42    if !path.is_file() {
43        return Ok(None);
44    }
45    let text = std::fs::read_to_string(path)
46        .with_context(|| format!("reading analysis cache {}", path.display()))?;
47    let model: ModelIr = serde_json::from_str(&text).context("parsing cached model IR")?;
48    Ok(Some(model))
49}
50
51pub fn save(path: &Path, model: &ModelIr) -> Result<()> {
52    if let Some(parent) = path.parent() {
53        std::fs::create_dir_all(parent)
54            .with_context(|| format!("creating cache dir {}", parent.display()))?;
55    }
56    let text = serde_json::to_string(model).context("serializing model IR for cache")?;
57    std::fs::write(path, text)
58        .with_context(|| format!("writing analysis cache {}", path.display()))?;
59    Ok(())
60}