1use serde::Deserialize;
7use std::path::{Path, PathBuf};
8
9const CORPUS_REL: &str = "tests/corpus";
11
12pub fn corpus_root() -> PathBuf {
17 let manifest_dir =
20 std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set — run via cargo");
21 let mut dir = PathBuf::from(&manifest_dir);
22 loop {
23 let candidate = dir.join(CORPUS_REL);
24 if candidate.is_dir() {
25 return candidate;
26 }
27 if !dir.pop() {
28 panic!(
29 "could not find {CORPUS_REL}/ in any ancestor of {manifest_dir}; \
30 is the workspace root missing tests/corpus/?"
31 );
32 }
33 }
34}
35
36pub fn fixtures_in(subdir: &str) -> Vec<PathBuf> {
38 let dir = corpus_root().join(subdir);
39 if !dir.is_dir() {
40 return Vec::new();
41 }
42 let mut paths: Vec<PathBuf> = std::fs::read_dir(&dir)
43 .expect("failed to read corpus directory")
44 .filter_map(|e| e.ok())
45 .map(|e| e.path())
46 .filter(|p| p.extension().is_some_and(|ext| ext == "txt"))
47 .collect();
48 paths.sort();
49 paths
50}
51
52pub fn invalid_fixtures() -> Vec<PathBuf> {
54 fixtures_in("invalid")
55}
56
57pub fn valid_fixtures() -> Vec<PathBuf> {
59 fixtures_in("valid")
60}
61
62pub fn prose_fixtures() -> Vec<PathBuf> {
64 fixtures_in("prose")
65}
66
67#[derive(Debug, Clone, Deserialize)]
69pub struct ExpectedDiagnostic {
70 pub rule: String,
71 pub span: ExpectedSpan,
72 #[serde(default)]
73 pub severity: Option<String>,
74}
75
76#[derive(Debug, Clone, Deserialize)]
78pub struct ExpectedSpan {
79 pub start: usize,
80 pub end: usize,
81}
82
83#[derive(Debug, Clone, Deserialize)]
85pub struct ExpectedFixture {
86 pub diagnostics: Vec<ExpectedDiagnostic>,
87}
88
89pub fn load_expected(fixture_path: &Path) -> ExpectedFixture {
94 let json_path = fixture_path.with_extension("expected.json");
95 if !json_path.exists() {
96 panic!(
97 "missing expected file for fixture: {} (expected {})",
98 fixture_path.display(),
99 json_path.display()
100 );
101 }
102 let content = std::fs::read_to_string(&json_path)
103 .unwrap_or_else(|e| panic!("failed to read {}: {e}", json_path.display()));
104 serde_json::from_str(&content)
105 .unwrap_or_else(|e| panic!("failed to parse {}: {e}", json_path.display()))
106}
107
108pub fn load_fixture(path: &Path) -> Vec<u8> {
110 std::fs::read(path).unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display()))
111}