use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
use serde::Serialize;
use crate::repo::{git_common_dir, scout_dir};
pub(crate) fn graph_dir(root: &Path) -> PathBuf {
match git_common_dir(root) {
Some(common) => common.join("scout").join("graph"),
None => scout_dir(root).join("graph"),
}
}
pub fn graph_json_path(root: &Path) -> PathBuf {
graph_dir(root).join("graph.json")
}
pub fn project_units_path(root: &Path) -> PathBuf {
graph_dir(root).join("project-units.json")
}
pub(crate) fn fragments_cache_path(root: &Path) -> PathBuf {
graph_dir(root).join("fragments-v19.json")
}
pub(crate) fn fragments_index_path(root: &Path) -> PathBuf {
graph_dir(root).join("fragments-index-v19.json")
}
pub(crate) const SUPERSEDED_CACHE_FILES: &[&str] = &[
"fragments.json",
"fragments-index.json",
"fragments-v2.json",
"fragments-index-v2.json",
"fragments-v3.json",
"fragments-index-v3.json",
"fragments-v4.json",
"fragments-index-v4.json",
"fragments-v5.json",
"fragments-index-v5.json",
"fragments-v6.json",
"fragments-index-v6.json",
"fragments-v7.json",
"fragments-index-v7.json",
"fragments-v8.json",
"fragments-index-v8.json",
"fragments-v9.json",
"fragments-index-v9.json",
"fragments-v10.json",
"fragments-index-v10.json",
"fragments-v11.json",
"fragments-index-v11.json",
"fragments-v12.json",
"fragments-index-v12.json",
"fragments-v13.json",
"fragments-index-v13.json",
"fragments-v14.json",
"fragments-index-v14.json",
"fragments-v15.json",
"fragments-index-v15.json",
"fragments-v16.json",
"fragments-index-v16.json",
"fragments-v17.json",
"fragments-index-v17.json",
"fragments-v18.json",
"fragments-index-v18.json",
];
pub(crate) fn remove_superseded_caches(root: &Path) {
for name in SUPERSEDED_CACHE_FILES {
let _ = fs::remove_file(graph_dir(root).join(name));
}
}
static TMP_COUNTER: AtomicU64 = AtomicU64::new(0);
pub(crate) fn atomic_write_bytes(path: &Path, bytes: &[u8]) -> io::Result<()> {
let dir = path.parent().unwrap_or_else(|| Path::new("."));
fs::create_dir_all(dir)?;
let file_name = path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("artifact");
let counter = TMP_COUNTER.fetch_add(1, Ordering::Relaxed);
let tmp_path = dir.join(format!(".{file_name}.tmp.{}.{counter}", std::process::id()));
let write_result = fs::write(&tmp_path, bytes);
if write_result.is_err() {
let _ = fs::remove_file(&tmp_path);
write_result?;
}
fs::rename(&tmp_path, path)?;
Ok(())
}
pub(crate) fn atomic_write_json<T: Serialize>(path: &Path, value: &T) -> io::Result<()> {
let bytes = serde_json::to_vec(value).map_err(io::Error::other)?;
atomic_write_bytes(path, &bytes)
}