use std::path::{Path, PathBuf};
use crate::analysis_context::resolve_programmatic_analysis_context;
use crate::{AnalysisOptions, ProgrammaticError};
const LAST_REPORT_FILE: &str = "last-report.json";
pub fn ignore_cache_dir(cache_dir: &Path) -> std::io::Result<()> {
std::fs::create_dir_all(cache_dir)?;
let ignore = cache_dir.join(".gitignore");
if !ignore.exists() {
std::fs::write(ignore, "*\n")?;
}
Ok(())
}
fn cache_error(message: String) -> ProgrammaticError {
ProgrammaticError::new(message, 2).with_code("FALLOW_MUTATION_CACHE_WRITE_FAILED")
}
fn cache_dir(analysis: &AnalysisOptions) -> Result<PathBuf, ProgrammaticError> {
let resolved = resolve_programmatic_analysis_context(analysis)?;
let project = fallow_engine::project_config::config_for_project_with_load_options(
resolved.root(),
resolved.config_path().as_deref(),
fallow_config::ConfigLoadOptions {
allow_remote_extends: resolved.allow_remote_extends(),
},
)
.map_err(|error| {
ProgrammaticError::new(format!("failed to load config: {error}"), 2)
.with_code("FALLOW_CONFIG_LOAD_FAILED")
.with_context("analysis.configPath")
})?;
Ok(project.config.cache_dir)
}
pub fn last_mutation_report_path(analysis: &AnalysisOptions) -> Result<PathBuf, ProgrammaticError> {
Ok(cache_dir(analysis)?.join("mutation").join(LAST_REPORT_FILE))
}
pub fn save_last_mutation_report(
analysis: &AnalysisOptions,
report: &serde_json::Value,
) -> Result<PathBuf, ProgrammaticError> {
let cache_dir = cache_dir(analysis)?;
ignore_cache_dir(&cache_dir).map_err(|error| cache_error(error.to_string()))?;
let dir = cache_dir.join("mutation");
std::fs::create_dir_all(&dir).map_err(|error| cache_error(error.to_string()))?;
let bytes = serde_json::to_vec(report)
.map_err(|error| cache_error(format!("failed to serialize the report: {error}")))?;
let path = dir.join(LAST_REPORT_FILE);
let temporary = dir.join(format!("{LAST_REPORT_FILE}.{}.tmp", std::process::id()));
std::fs::write(&temporary, bytes)
.and_then(|()| std::fs::rename(&temporary, &path))
.map_err(|error| {
let _ = std::fs::remove_file(&temporary);
cache_error(format!("failed to save {}: {error}", path.display()))
})?;
Ok(path)
}