use serde::Deserialize;
use std::path::{Path, PathBuf};
const CORPUS_REL: &str = "tests/corpus";
pub fn corpus_root() -> PathBuf {
let manifest_dir =
std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set — run via cargo");
let mut dir = PathBuf::from(&manifest_dir);
loop {
let candidate = dir.join(CORPUS_REL);
if candidate.is_dir() {
return candidate;
}
if !dir.pop() {
panic!(
"could not find {CORPUS_REL}/ in any ancestor of {manifest_dir}; \
is the workspace root missing tests/corpus/?"
);
}
}
}
pub fn fixtures_in(subdir: &str) -> Vec<PathBuf> {
let dir = corpus_root().join(subdir);
if !dir.is_dir() {
return Vec::new();
}
let mut paths: Vec<PathBuf> = std::fs::read_dir(&dir)
.expect("failed to read corpus directory")
.filter_map(|e| e.ok())
.map(|e| e.path())
.filter(|p| p.extension().is_some_and(|ext| ext == "txt"))
.collect();
paths.sort();
paths
}
pub fn invalid_fixtures() -> Vec<PathBuf> {
fixtures_in("invalid")
}
pub fn valid_fixtures() -> Vec<PathBuf> {
fixtures_in("valid")
}
pub fn prose_fixtures() -> Vec<PathBuf> {
fixtures_in("prose")
}
#[derive(Debug, Clone, Deserialize)]
pub struct ExpectedDiagnostic {
pub rule: String,
pub span: ExpectedSpan,
#[serde(default)]
pub severity: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ExpectedSpan {
pub start: usize,
pub end: usize,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ExpectedFixture {
pub diagnostics: Vec<ExpectedDiagnostic>,
}
pub fn load_expected(fixture_path: &Path) -> ExpectedFixture {
let json_path = fixture_path.with_extension("expected.json");
if !json_path.exists() {
panic!(
"missing expected file for fixture: {} (expected {})",
fixture_path.display(),
json_path.display()
);
}
let content = std::fs::read_to_string(&json_path)
.unwrap_or_else(|e| panic!("failed to read {}: {e}", json_path.display()));
serde_json::from_str(&content)
.unwrap_or_else(|e| panic!("failed to parse {}: {e}", json_path.display()))
}
pub fn load_fixture(path: &Path) -> Vec<u8> {
std::fs::read(path).unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display()))
}