use crate::output::{self, OutputFormat};
use camino::{Utf8Path, Utf8PathBuf};
use provenance_store::{layout::ProvenanceLayout, state_store::StateStore};
use std::collections::BTreeSet;
mod edges;
mod index;
mod references;
mod scope;
mod statement_report;
use index::CheckIndex;
pub(super) fn check(repo: &Utf8Path, format: OutputFormat) -> anyhow::Result<()> {
let store = StateStore::new(ProvenanceLayout::new(repo.to_path_buf()));
let report = store.with_repository_publication(|| collect_report_locked(&store, repo))?;
output::print(format, &report)
}
#[derive(serde::Serialize)]
struct CheckReport {
status: &'static str,
diagnostics: Vec<provenance_store::statement_analysis::StatementDiagnostic>,
}
pub(super) fn validate_repository(repo: Utf8PathBuf) -> anyhow::Result<()> {
let store = StateStore::new(ProvenanceLayout::new(repo));
store.with_repository_publication(|| validate_locked(&store))
}
fn collect_report_locked(store: &StateStore, repo: &Utf8Path) -> anyhow::Result<CheckReport> {
validate_locked(store)?;
Ok(CheckReport {
status: "ok",
diagnostics: statement_report::changed_statements_from_head(store, repo)?,
})
}
fn validate_locked(store: &StateStore) -> anyhow::Result<()> {
let manifest = store.manifest()?;
anyhow::ensure!(
!manifest.scopes.is_empty(),
"manifest must contain at least one scope"
);
let manifest_scopes: BTreeSet<_> = manifest
.scopes
.iter()
.map(|scope| scope.id.as_str().to_string())
.collect();
let scope_directory_findings = store
.list_scope_directories()?
.into_iter()
.filter(|directory| !manifest_scopes.contains(directory))
.map(|directory| format!("scope directory {directory} is absent from manifest"))
.collect::<Vec<_>>();
let mut index = CheckIndex::default();
let mut dangling = Vec::new();
scope::validate(
store,
&manifest.scopes,
&manifest_scopes,
&mut index,
&mut dangling,
)?;
edges::validate(store, &manifest_scopes, &index, &mut dangling)?;
anyhow::ensure!(
scope_directory_findings.is_empty(),
"scope directory finding(s):\n- {}",
scope_directory_findings.join("\n- ")
);
anyhow::ensure!(
dangling.is_empty(),
"dangling reference(s):\n- {}",
dangling.join("\n- ")
);
Ok(())
}