1pub use crate::model::ToolInfo;
2use serde::{Deserialize, Serialize};
3use std::collections::BTreeMap;
4use std::collections::hash_map::DefaultHasher;
5use std::hash::{Hash, Hasher};
6use std::path::{Path, PathBuf};
7
8#[derive(Debug, Default, Serialize, Deserialize)]
9struct CacheFile {
10 config_hash: u64,
11 servers: BTreeMap<String, Vec<ToolInfo>>,
12 instructions: BTreeMap<String, String>,
13}
14
15#[derive(Debug, Default)]
16pub struct Cache {
17 pub servers: BTreeMap<String, Vec<ToolInfo>>,
18 pub instructions: BTreeMap<String, String>,
19}
20
21pub fn config_hash(text: &str) -> u64 {
22 let mut h = DefaultHasher::new();
24 text.hash(&mut h);
25 h.finish()
26}
27
28pub fn cache_dir() -> PathBuf {
29 let base = std::env::var_os("XDG_CACHE_HOME")
30 .map(PathBuf::from)
31 .or_else(|| std::env::var_os("HOME").map(|h| PathBuf::from(h).join(".cache")))
32 .unwrap_or_else(|| PathBuf::from("."));
33 base.join("mcp-multiplexer")
34}
35
36pub fn cache_path() -> PathBuf {
37 cache_dir().join("index.json")
38}
39
40impl Cache {
41 pub fn load(config_hash: u64) -> Cache {
42 Self::load_from(&cache_path(), config_hash)
43 }
44 pub fn save(&self, config_hash: u64) -> anyhow::Result<()> {
45 self.save_to(&cache_path(), config_hash)
46 }
47
48 pub fn load_from(path: &Path, config_hash: u64) -> Cache {
49 let Ok(text) = std::fs::read_to_string(path) else {
50 return Cache::default();
51 };
52 let Ok(f) = serde_json::from_str::<CacheFile>(&text) else {
53 return Cache::default();
54 };
55 if f.config_hash != config_hash {
56 return Cache::default();
57 }
58 Cache {
59 servers: f.servers,
60 instructions: f.instructions,
61 }
62 }
63
64 pub fn save_to(&self, path: &Path, config_hash: u64) -> anyhow::Result<()> {
65 if let Some(dir) = path.parent() {
66 std::fs::create_dir_all(dir)?;
67 }
68 let f = CacheFile {
69 config_hash,
70 servers: self.servers.clone(),
71 instructions: self.instructions.clone(),
72 };
73 let tmp = path.with_extension("json.tmp");
74 std::fs::write(&tmp, serde_json::to_string(&f)?)?;
75 std::fs::rename(&tmp, path)?;
76 Ok(())
77 }
78}