use std::path::{Path, PathBuf};
use std::sync::{Mutex, OnceLock};
use fallow_output::{RequestName, RequestOutcome, RequestOutcomes, RequestStatus};
use rustc_hash::FxHashSet;
static CHANGED_SINCE_OUTCOME: OnceLock<RequestOutcome> = OnceLock::new();
static CHANGED_SINCE_FILES: OnceLock<FxHashSet<PathBuf>> = OnceLock::new();
static CHANGED_SINCE_ANALYZED: Mutex<Option<FxHashSet<PathBuf>>> = Mutex::new(None);
pub fn resolve_changed_since(root: &Path, git_ref: &str) -> Option<FxHashSet<PathBuf>> {
match fallow_engine::changed_files::changed_files(root, git_ref) {
Ok(files) => {
record_changed_since(RequestOutcome::applied(RequestName::ChangedSince, git_ref));
let _ = CHANGED_SINCE_FILES.set(
files
.iter()
.map(|path| dunce::simplified(path).to_path_buf())
.collect(),
);
Some(files)
}
Err(err) => {
let message = err.changed_since_message(git_ref);
eprintln!("Warning: {message}");
record_changed_since(RequestOutcome::not_applied(
RequestName::ChangedSince,
git_ref,
err.reason(),
message,
));
None
}
}
}
fn record_changed_since(outcome: RequestOutcome) {
let _ = CHANGED_SINCE_OUTCOME.set(outcome);
}
pub fn measure_changed_since_scope(analyzed: &[fallow_types::discover::DiscoveredFile]) {
let Some(changed) = CHANGED_SINCE_FILES.get() else {
return;
};
let Ok(mut union) = CHANGED_SINCE_ANALYZED.lock() else {
return;
};
let union = union.get_or_insert_with(FxHashSet::default);
union.extend(
analyzed
.iter()
.map(|file| dunce::simplified(&file.path))
.filter(|path| changed.contains(*path))
.map(Path::to_path_buf),
);
}
fn changed_since_outcome() -> Option<RequestOutcome> {
let outcome = CHANGED_SINCE_OUTCOME.get()?.clone();
let size = CHANGED_SINCE_ANALYZED
.lock()
.ok()
.and_then(|union| union.as_ref().map(|files| files.len() as u64));
Some(match size {
Some(size) if outcome.status == RequestStatus::Applied => RequestOutcome {
scope_size: Some(size),
..outcome
},
_ => outcome,
})
}
static SARIF_FILE_OUTCOME: OnceLock<RequestOutcome> = OnceLock::new();
pub fn record_sarif_file_applied(path: &Path) {
let _ = SARIF_FILE_OUTCOME.set(RequestOutcome::applied(
RequestName::SarifFile,
path.display().to_string(),
));
}
pub fn record_sarif_file_failure(path: &Path, reason: &str, message: String) {
let _ = SARIF_FILE_OUTCOME.set(RequestOutcome::not_applied(
RequestName::SarifFile,
path.display().to_string(),
reason,
message,
));
}
#[must_use]
pub fn changed_since_request_outcomes() -> Option<RequestOutcomes> {
let mut requests = RequestOutcomes::new();
requests.insert_if(RequestName::ChangedSince, changed_since_outcome());
requests.into_option()
}
#[must_use]
pub fn request_outcomes() -> Option<RequestOutcomes> {
let mut requests = RequestOutcomes::new();
requests.insert_if(RequestName::ChangedSince, changed_since_outcome());
requests.insert_if(
RequestName::DiffFilter,
crate::report::ci::diff_filter::shared_diff_request_outcome().cloned(),
);
requests.insert_if(RequestName::SarifFile, SARIF_FILE_OUTCOME.get().cloned());
requests.into_option()
}