use crate::OpSpec;
use super::error::CoverageError;
pub(super) fn check_docs_path(spec: &OpSpec, errors: &mut Vec<CoverageError>) {
if spec.docs_path.trim().is_empty() {
errors.push(CoverageError::MissingDocsPath {
op_id: spec.id.to_string(),
});
return;
}
if !spec.docs_path.starts_with("docs/ops/")
|| spec.docs_path.split(['/', '\\']).any(|part| part == "..")
{
errors.push(CoverageError::InvalidDocsPath {
op_id: spec.id.to_string(),
});
return;
}
match std::fs::metadata(spec.docs_path) {
Ok(meta) if meta.is_file() => {
let mut buf = [0u8; 4096];
let n = match std::fs::File::open(spec.docs_path)
.and_then(|mut f| std::io::Read::read(&mut f, &mut buf))
{
Ok(n) => n,
Err(_) => {
errors.push(CoverageError::MissingDocsFile {
op_id: spec.id.to_string(),
path: spec.docs_path.to_string(),
});
return;
}
};
let preview = &buf[..n];
if preview.is_empty()
|| !preview
.windows(spec.id.len())
.any(|w| w == spec.id.as_bytes())
{
errors.push(CoverageError::InvalidDocsPath {
op_id: spec.id.to_string(),
});
}
}
_ => {
errors.push(CoverageError::MissingDocsFile {
op_id: spec.id.to_string(),
path: spec.docs_path.to_string(),
});
}
}
}