use crate::{Diagnostic, ValidationLayer, diagnostic, validator};
use knowledge_base_snapshot::RepositorySnapshot;
use std::path::{Path, PathBuf};
#[derive(Clone, Copy, Debug)]
pub struct ValidationContext<'a> {
repository_root: &'a Path,
snapshot: &'a RepositorySnapshot,
}
impl<'a> ValidationContext<'a> {
fn new(repository_root: &'a Path, snapshot: &'a RepositorySnapshot) -> Self {
Self { repository_root, snapshot }
}
pub fn repository_root(&self) -> &'a Path {
self.repository_root
}
pub fn snapshot(&self) -> &'a RepositorySnapshot {
self.snapshot
}
}
pub trait KnowledgeBaseValidator: Send + Sync {
fn validate(&self, context: &ValidationContext<'_>) -> Vec<Diagnostic>;
}
impl<F> KnowledgeBaseValidator for F
where
F: for<'a> Fn(&ValidationContext<'a>) -> Vec<Diagnostic> + Send + Sync,
{
fn validate(&self, context: &ValidationContext<'_>) -> Vec<Diagnostic> {
self(context)
}
}
pub fn validate_repository_with<'a>(root: impl AsRef<Path>, validators: impl IntoIterator<Item = &'a dyn KnowledgeBaseValidator>) -> Vec<Diagnostic> {
let root = root.as_ref();
let mut diagnostics = validator::validate_repository(root);
let validators = validators.into_iter().collect::<Vec<_>>();
if !validators.is_empty() {
match RepositorySnapshot::load(root) {
Ok(snapshot) => {
let context = ValidationContext::new(root, &snapshot);
for validator in validators {
diagnostics.extend(validator.validate(&context));
}
}
Err(error) if diagnostics.is_empty() => {
let path = error.path().strip_prefix(root).map(PathBuf::from).unwrap_or_else(|_| error.path().to_path_buf());
diagnostics.push(Diagnostic {
layer: ValidationLayer::Schema,
path,
line: None,
identifier: None,
message: format!("cannot load shared repository snapshot: {error}"),
});
}
Err(_) => {}
}
}
diagnostic::sort_diagnostics(&mut diagnostics);
diagnostics
}