use std::path::{Path, PathBuf};
use crate::artifacts::{ArtifactSet, Loaded};
use crate::pipeline::Error;
use crate::validate::{Context, Finding, render_coverage_report, validate};
#[derive(Debug)]
pub struct Validation {
pub loaded: Loaded,
pub findings: Vec<Finding>,
}
impl Validation {
#[must_use]
pub fn is_clean(&self) -> bool {
self.findings.is_empty()
}
}
pub fn validate_tree(root: &Path, specs: Option<&Path>) -> Result<Validation, Error> {
let loaded = crate::artifacts::load_root(root).map_err(Error::Catalogue)?;
let findings = validate(&Context {
set: &loaded.set,
load_errors: &loaded.errors,
spec_root: specs,
});
Ok(Validation { loaded, findings })
}
#[must_use]
pub fn coverage_report_path(root: &Path) -> PathBuf {
root.join("coverage-report.md")
}
pub fn write_coverage_report(set: &ArtifactSet, specs: &Path, path: &Path) -> Result<(), Error> {
let body = render_coverage_report(set, Some(specs));
path.parent()
.map_or(Ok(()), std::fs::create_dir_all)
.and_then(|()| std::fs::write(path, body))
.map_err(|source| Error::Write {
path: path.to_owned(),
source,
})
}
#[cfg(test)]
#[expect(
clippy::panic_in_result_fn,
reason = "Result-returning tests in the Book ch11 shape, each asserting; \
clippy offers no allow-in-tests knob for this lint"
)]
mod tests {
use super::{coverage_report_path, write_coverage_report};
use crate::artifacts::load_root;
use std::path::{Path, PathBuf};
fn repo_root() -> PathBuf {
Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/../..")).to_path_buf()
}
#[test]
fn the_report_home_is_a_directory_this_repository_has() {
let root = repo_root().join("artifacts");
let path = coverage_report_path(&root);
assert_eq!(path, root.join("coverage-report.md"));
let parent = path.parent().expect("a joined path should have a parent");
assert!(
parent.is_dir(),
"the coverage report's home {} must be a directory that exists",
parent.display()
);
}
#[test]
fn the_report_never_escapes_the_root_it_describes() {
for root in [
Path::new("artifacts"),
Path::new("/srv/some/other/catalogue"),
Path::new("../relative/root"),
] {
let path = coverage_report_path(root);
assert!(
path.starts_with(root),
"{} escaped its root {}",
path.display(),
root.display()
);
assert_eq!(path.parent(), Some(root));
}
}
#[test]
fn write_coverage_report_writes_at_the_derived_path() -> Result<(), Box<dyn std::error::Error>>
{
let out = assert_fs::TempDir::new()?;
let loaded = load_root(&repo_root().join("artifacts"))?;
let path = coverage_report_path(out.path());
write_coverage_report(&loaded.set, &repo_root().join("specs/openehr"), &path)?;
assert!(path.is_file(), "{} was not written", path.display());
assert!(!std::fs::read_to_string(&path)?.is_empty());
Ok(())
}
}