Krolyxon
Commit 24133e0

Branches

Refs →

budctlcratesbudctl-coretestsguards.rs

76 lines · 2.6 KB · Rust

1//! Checks for the CLAUDE.md hard rules that the compiler and clippy cannot make on their own.
2//!
3//! - Rule 3: every `Spec: <file>.md#<anchor>` comment points at an anchor that exists.
4//!
5//! The CLI's exit codes are checked in crates/budctl-cli/tests/cli.rs.
6
7// Test-only file: helpers may panic (clippy.toml exempts only #[test] functions themselves).
8#![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing)]
9
10use std::fs;
11use std::path::{Path, PathBuf};
12
13fn workspace() -> PathBuf {
14 Path::new(env!("CARGO_MANIFEST_DIR")).join("../..")
15}
16
17/// Every `.rs` file under `crates/*/src`.
18fn sources() -> Vec<PathBuf> {
19 fn walk(dir: &Path, out: &mut Vec<PathBuf>) {
20 for entry in fs::read_dir(dir).unwrap() {
21 let path = entry.unwrap().path();
22 if path.is_dir() {
23 walk(&path, out);
24 } else if path.extension().is_some_and(|e| e == "rs") {
25 out.push(path);
26 }
27 }
28 }
29 let mut files = Vec::new();
30 for krate in fs::read_dir(workspace().join("crates")).unwrap() {
31 let src = krate.unwrap().path().join("src");
32 if src.is_dir() {
33 walk(&src, &mut files);
34 }
35 }
36 assert!(!files.is_empty(), "found no sources to check");
37 files
38}
39
40#[test]
41fn spec_references_point_at_existing_anchors() {
42 let docs = workspace().join("docs/protocol");
43 let marker = "Spec: ";
44 let mut checked = 0;
45 let mut broken = Vec::new();
46 for file in sources() {
47 let text = fs::read_to_string(&file).unwrap();
48 for (n, line) in text.lines().enumerate() {
49 let mut rest = line;
50 while let Some(at) = rest.find(marker) {
51 rest = &rest[at + marker.len()..];
52 let reference: String = rest
53 .chars()
54 .take_while(|c| c.is_ascii_alphanumeric() || "._-#".contains(*c))
55 .collect();
56 let Some((doc, anchor)) = reference.split_once('#') else {
57 continue;
58 };
59 if !doc.ends_with(".md") {
60 continue;
61 }
62 checked += 1;
63 let spec = fs::read_to_string(docs.join(doc)).unwrap_or_default();
64 if !spec.contains(&format!("id=\"{anchor}\"")) {
65 broken.push(format!("{}:{}: {reference}", file.display(), n + 1));
66 }
67 }
68 }
69 }
70 assert!(checked > 0, "found no Spec: references at all");
71 assert!(
72 broken.is_empty(),
73 "Spec: references with no matching anchor:\n{}",
74 broken.join("\n")
75 );
76}