spec_driven_docs/services/
known_issues.rs1use camino::Utf8Path;
10use serde::Serialize;
11
12use crate::error::AppError;
13use crate::gates::front_matter_values;
14use crate::gates::paths::ki_records;
15use crate::gates::{GateCtx, read_text};
16
17#[derive(Debug, Serialize)]
19pub struct Case {
20 pub id: String,
22 pub state: Option<String>,
24 pub filing: Option<String>,
26 pub upstream: Option<String>,
28 pub path: String,
30}
31
32pub fn cases(target: &Utf8Path) -> Result<Vec<Case>, AppError> {
38 let ctx = GateCtx::new(target);
39 let mut cases = Vec::new();
40 for record in ki_records(&ctx, &[])? {
41 let text = read_text(&ctx, &record)?;
42 cases.push(Case {
43 id: record.file_stem().unwrap_or_default().to_string(),
44 state: first(&text, "state"),
45 filing: first(&text, "filing"),
46 upstream: first(&text, "upstream"),
47 path: record.to_string(),
48 });
49 }
50 Ok(cases)
51}
52
53fn first(text: &str, key: &str) -> Option<String> {
55 front_matter_values(text, key)
56 .into_iter()
57 .find(|value| !value.is_empty())
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 fn target_with(record: &str) -> tempfile::TempDir {
65 let dir = tempfile::tempdir().unwrap();
66 let records = dir.path().join("_docs/reference/known-issues");
67 std::fs::create_dir_all(&records).unwrap();
68 std::fs::write(records.join("KI-vendor-replays.md"), record).unwrap();
69 dir
70 }
71
72 #[test]
73 fn reads_both_axes_and_the_upstream_reference() {
74 let dir = target_with(
75 "---\nupstream: https://example.invalid/issues/1\nstate: masked\nfiling: filed\n---\n# V\n",
76 );
77 let target = camino::Utf8Path::from_path(dir.path()).unwrap();
78 let cases = cases(target).unwrap();
79 assert_eq!(cases.len(), 1);
80 assert_eq!(cases[0].id, "KI-vendor-replays");
81 assert_eq!(cases[0].state.as_deref(), Some("masked"));
82 assert_eq!(cases[0].filing.as_deref(), Some("filed"));
83 assert_eq!(
84 cases[0].path,
85 "_docs/reference/known-issues/KI-vendor-replays.md"
86 );
87 }
88
89 #[test]
90 fn a_record_stating_no_axis_reports_none_rather_than_failing() {
91 let dir = target_with("---\naffects: client\n---\n# V\n");
92 let target = camino::Utf8Path::from_path(dir.path()).unwrap();
93 let cases = cases(target).unwrap();
94 assert_eq!(cases.len(), 1);
95 assert!(cases[0].state.is_none());
96 assert!(cases[0].filing.is_none());
97 assert!(cases[0].upstream.is_none());
98 }
99
100 #[test]
101 fn a_target_with_no_zone_lists_nothing() {
102 let dir = tempfile::tempdir().unwrap();
103 let target = camino::Utf8Path::from_path(dir.path()).unwrap();
104 assert!(cases(target).unwrap().is_empty());
105 }
106}