fallow_api/mutation/
last_report.rs1use std::path::{Path, PathBuf};
10
11use crate::analysis_context::resolve_programmatic_analysis_context;
12use crate::{AnalysisOptions, ProgrammaticError};
13
14const LAST_REPORT_FILE: &str = "last-report.json";
16
17pub fn ignore_cache_dir(cache_dir: &Path) -> std::io::Result<()> {
20 std::fs::create_dir_all(cache_dir)?;
21 let ignore = cache_dir.join(".gitignore");
22 if !ignore.exists() {
23 std::fs::write(ignore, "*\n")?;
24 }
25 Ok(())
26}
27
28fn cache_error(message: String) -> ProgrammaticError {
29 ProgrammaticError::new(message, 2).with_code("FALLOW_MUTATION_CACHE_WRITE_FAILED")
30}
31
32fn cache_dir(analysis: &AnalysisOptions) -> Result<PathBuf, ProgrammaticError> {
34 let resolved = resolve_programmatic_analysis_context(analysis)?;
35 let project = fallow_engine::project_config::config_for_project_with_load_options(
36 resolved.root(),
37 resolved.config_path().as_deref(),
38 fallow_config::ConfigLoadOptions {
39 allow_remote_extends: resolved.allow_remote_extends(),
40 },
41 )
42 .map_err(|error| {
43 ProgrammaticError::new(format!("failed to load config: {error}"), 2)
44 .with_code("FALLOW_CONFIG_LOAD_FAILED")
45 .with_context("analysis.configPath")
46 })?;
47 Ok(project.config.cache_dir)
48}
49
50pub fn last_mutation_report_path(analysis: &AnalysisOptions) -> Result<PathBuf, ProgrammaticError> {
56 Ok(cache_dir(analysis)?.join("mutation").join(LAST_REPORT_FILE))
57}
58
59pub fn remove_finished_mutation_journal(
72 analysis: &AnalysisOptions,
73 report: &fallow_output::MutationReport,
74) -> Result<bool, ProgrammaticError> {
75 if report.completion.status != fallow_output::MutationCompletionStatus::Complete
76 || report.runner.is_none()
77 {
78 return Ok(false);
79 }
80 let path = super::journal::Journal::path(&cache_dir(analysis)?.join("mutation"));
81 match std::fs::remove_file(&path) {
82 Ok(()) => Ok(true),
83 Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false),
84 Err(error) => Err(cache_error(format!(
85 "failed to remove {}: {error}",
86 path.display()
87 ))),
88 }
89}
90
91pub fn save_last_mutation_report(
100 analysis: &AnalysisOptions,
101 report: &serde_json::Value,
102) -> Result<PathBuf, ProgrammaticError> {
103 let cache_dir = cache_dir(analysis)?;
104 ignore_cache_dir(&cache_dir).map_err(|error| cache_error(error.to_string()))?;
105 let dir = cache_dir.join("mutation");
106 std::fs::create_dir_all(&dir).map_err(|error| cache_error(error.to_string()))?;
107 let bytes = serde_json::to_vec(report)
108 .map_err(|error| cache_error(format!("failed to serialize the report: {error}")))?;
109 let path = dir.join(LAST_REPORT_FILE);
110 let temporary = dir.join(format!("{LAST_REPORT_FILE}.{}.tmp", std::process::id()));
111 std::fs::write(&temporary, bytes)
112 .and_then(|()| std::fs::rename(&temporary, &path))
113 .map_err(|error| {
114 let _ = std::fs::remove_file(&temporary);
115 cache_error(format!("failed to save {}: {error}", path.display()))
116 })?;
117 Ok(path)
118}