use std::{
collections::HashMap,
fs::File,
path::{Path, PathBuf},
time::SystemTime,
};
use anyhow::Context;
use serde::{Deserialize, Serialize};
use crate::{ext::ResultExt, symbol::Symbol};
const CACHE_FILE_NAME: &str = "cache.json";
#[derive(Default)]
pub struct Cache {
path: Option<PathBuf>,
files: HashMap<PathBuf, FileInfo>,
}
#[derive(Serialize, Deserialize)]
pub struct FileInfo {
pub modified: SystemTime,
pub symbols: Vec<Symbol<(), String>>,
}
impl Cache {
pub fn from_dir<P: AsRef<Path>>(path: P) -> Result<Self, anyhow::Error> {
let path = path.as_ref().join(CACHE_FILE_NAME);
if !path.exists() {
std::fs::create_dir_all(path.parent().context("parent")?).context("create dir")?;
return Ok(Self {
path: Some(path.clone()),
files: Default::default(),
});
}
let file = File::open(&path).context("open")?;
Ok(Self {
path: Some(path.clone()),
files: serde_json::from_reader(file)
.context("failed to parse cache")
.warn()
.unwrap_or_default(),
})
}
pub fn get_file_info(&self, path: &PathBuf) -> Option<&FileInfo> {
self.files.get(path)
}
pub fn insert_file_info(&mut self, path: PathBuf, modified: SystemTime) {
self.files.insert(
path,
FileInfo {
modified,
symbols: Vec::new(),
},
);
}
pub fn insert_symbol<P, T: Into<String>>(&mut self, path: &Path, symbol: Symbol<P, T>) -> Result<(), anyhow::Error> {
self
.files
.get_mut(path)
.with_context(|| format!("inserting symbol into unknown path: {path:?}"))?
.symbols
.push(symbol.forget_path());
Ok(())
}
pub fn save(&self) -> Result<(), anyhow::Error> {
let Some(path) = &self.path else {
return Ok(());
};
let json = serde_json::to_string(&self.files).context("to_string")?;
std::fs::write(path, json).context("write")
}
}