use std::fs;
use std::io::{self, Write};
use std::path::PathBuf;
use brazen::{CachedModels, ModelCache};
pub struct XdgModelCache {
pub(super) dir: Option<PathBuf>,
}
impl XdgModelCache {
pub fn new() -> Self {
XdgModelCache { dir: models_dir() }
}
fn path(&self, provider: &str) -> Option<PathBuf> {
self.dir
.as_ref()
.map(|d| d.join(format!("{provider}.json")))
}
fn write(&self, provider: &str, cached: &CachedModels) -> io::Result<()> {
let path = self
.path(provider)
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "no cache dir for models"))?;
let dir = path
.parent()
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "no models directory"))?;
fs::create_dir_all(dir)?;
let tmp = dir.join(format!(".{provider}.json.tmp"));
let bytes = serde_json::to_vec_pretty(cached)?;
let mut f = fs::File::create(&tmp)?;
f.write_all(&bytes)?;
f.sync_all()?;
fs::rename(&tmp, &path)
}
}
impl ModelCache for XdgModelCache {
fn get(&self, provider: &str) -> Option<CachedModels> {
let bytes = fs::read(self.path(provider)?).ok()?;
serde_json::from_slice(&bytes).ok()
}
fn put(&self, provider: &str, cached: &CachedModels) {
if let Err(e) = self.write(provider, cached) {
eprintln!("warning: could not write model cache for {provider}: {e}");
}
}
}
fn models_dir() -> Option<PathBuf> {
cache_dir().map(|d| d.join("brazen").join("models"))
}
#[cfg(target_os = "macos")]
pub(super) fn cache_dir() -> Option<PathBuf> {
std::env::var_os("HOME").map(|h| PathBuf::from(h).join("Library").join("Caches"))
}
#[cfg(target_os = "windows")]
pub(super) fn cache_dir() -> Option<PathBuf> {
std::env::var_os("LOCALAPPDATA").map(PathBuf::from)
}
#[cfg(all(unix, not(target_os = "macos")))]
pub(super) fn cache_dir() -> Option<PathBuf> {
std::env::var_os("XDG_CACHE_HOME")
.map(PathBuf::from)
.or_else(|| std::env::var_os("HOME").map(|h| PathBuf::from(h).join(".cache")))
}