use camino::Utf8Path;
use serde::Serialize;
use crate::error::AppError;
use crate::gates::front_matter_values;
use crate::gates::paths::ki_records;
use crate::gates::{GateCtx, read_text};
#[derive(Debug, Serialize)]
pub struct Case {
pub id: String,
pub state: Option<String>,
pub checked: Option<String>,
pub filing: Option<String>,
pub upstream: Option<String>,
pub path: String,
}
pub fn cases(target: &Utf8Path) -> Result<Vec<Case>, AppError> {
let ctx = GateCtx::new(target);
let mut cases = Vec::new();
for record in ki_records(&ctx, &[])? {
let text = read_text(&ctx, &record)?;
cases.push(Case {
id: record.file_stem().unwrap_or_default().to_string(),
state: first(&text, "state"),
checked: first(&text, "checked"),
filing: first(&text, "filing"),
upstream: first(&text, "upstream"),
path: record.to_string(),
});
}
Ok(cases)
}
fn first(text: &str, key: &str) -> Option<String> {
front_matter_values(text, key)
.into_iter()
.find(|value| !value.is_empty())
}
#[cfg(test)]
mod tests {
use super::*;
fn target_with(record: &str) -> tempfile::TempDir {
let dir = tempfile::tempdir().unwrap();
let records = dir.path().join("_docs/reference/known-issues");
std::fs::create_dir_all(&records).unwrap();
std::fs::write(records.join("KI-vendor-replays.md"), record).unwrap();
dir
}
#[test]
fn reads_both_axes_and_the_upstream_reference() {
let dir = target_with(
"---\nupstream: https://example.invalid/issues/1\nstate: masked\nchecked: 2026-06-18\nfiling: filed\n---\n# V\n",
);
let target = camino::Utf8Path::from_path(dir.path()).unwrap();
let cases = cases(target).unwrap();
assert_eq!(cases.len(), 1);
assert_eq!(cases[0].id, "KI-vendor-replays");
assert_eq!(cases[0].state.as_deref(), Some("masked"));
assert_eq!(cases[0].checked.as_deref(), Some("2026-06-18"));
assert_eq!(cases[0].filing.as_deref(), Some("filed"));
assert_eq!(
cases[0].path,
"_docs/reference/known-issues/KI-vendor-replays.md"
);
}
#[test]
fn a_record_stating_no_axis_reports_none_rather_than_failing() {
let dir = target_with("---\naffects: client\n---\n# V\n");
let target = camino::Utf8Path::from_path(dir.path()).unwrap();
let cases = cases(target).unwrap();
assert_eq!(cases.len(), 1);
assert!(cases[0].state.is_none());
assert!(cases[0].checked.is_none());
assert!(cases[0].filing.is_none());
assert!(cases[0].upstream.is_none());
}
#[test]
fn a_record_stating_an_empty_checked_reports_none() {
let dir = target_with("---\nstate: masked\nchecked:\n---\n# V\n");
let target = camino::Utf8Path::from_path(dir.path()).unwrap();
let cases = cases(target).unwrap();
assert_eq!(cases.len(), 1);
assert!(cases[0].checked.is_none());
}
#[test]
fn a_target_with_no_zone_lists_nothing() {
let dir = tempfile::tempdir().unwrap();
let target = camino::Utf8Path::from_path(dir.path()).unwrap();
assert!(cases(target).unwrap().is_empty());
}
}