use std::collections::BTreeSet;
const ERROR_RS: &str = include_str!("../src/error.rs");
const CONNECTION_RS: &str = include_str!("../src/connection.rs");
const SECTION_7: &str = include_str!("../docs/architecture/s6-s10-flows-to-dependencies.md");
const APPENDIX_A: &str = include_str!("../docs/architecture/appendices.md");
fn block_after<'a>(text: &'a str, header: &str) -> &'a str {
let start = text
.find(header)
.unwrap_or_else(|| panic!("{header:?} not found"))
+ header.len();
let rest = &text[start..];
let mut depth = 1usize;
for (i, c) in rest.char_indices() {
match c {
'{' => depth += 1,
'}' => {
depth -= 1;
if depth == 0 {
return &rest[..i];
}
}
_ => {}
}
}
panic!("unbalanced braces after {header:?}");
}
fn variant_names(body: &str) -> BTreeSet<String> {
body.lines()
.filter_map(|line| {
let trimmed = line.trim_start();
if trimmed.starts_with("//") || trimmed.starts_with('#') {
return None;
}
let indent = line.len() - trimmed.len();
if indent != 4 {
return None;
}
let name: String = trimmed
.chars()
.take_while(|c| c.is_alphanumeric() || *c == '_')
.collect();
match name.chars().next() {
Some(c) if c.is_ascii_uppercase() => Some(name),
_ => None,
}
})
.collect()
}
#[test]
fn the_documented_error_enum_matches_the_code() {
let in_code = variant_names(block_after(ERROR_RS, "pub enum DbError {"));
let in_docs = variant_names(block_after(SECTION_7, "pub enum DbError {"));
assert!(
in_code.len() >= 27,
"only {} variants parsed out of src/error.rs — the parser has broken, \
not the docs",
in_code.len()
);
assert_eq!(
in_code,
in_docs,
"§7's copy of DbError has drifted.\n missing from the docs: {:?}\n \
in the docs but not the code: {:?}\n\
§7 is a reproduction of src/error.rs; regenerate it rather than \
patching one variant.",
in_code.difference(&in_docs).collect::<Vec<_>>(),
in_docs.difference(&in_code).collect::<Vec<_>>(),
);
}
fn public_database_methods() -> BTreeSet<String> {
let mut out = BTreeSet::new();
for line in CONNECTION_RS.lines() {
let t = line.trim_start();
for prefix in ["pub async fn ", "pub fn "] {
if let Some(rest) = t.strip_prefix(prefix) {
let name: String = rest
.chars()
.take_while(|c| c.is_alphanumeric() || *c == '_')
.collect();
if !name.is_empty() {
out.insert(name);
}
}
}
}
out
}
#[test]
fn every_public_database_method_appears_in_appendix_a() {
const EXEMPT: &[(&str, &str)] = &[
(
"raw",
"#[doc(hidden)] — D-068/D-091: reachable, not advertised",
),
(
"new",
"constructors of other types in the same file, not handle methods",
),
(
"start",
"`Turn::start`, an internal actor helper in the same file",
),
("elapsed", "`HoldTimer::elapsed`, internal"),
("epoch", "`Turn::epoch`, internal"),
(
"estimated_bulk_hold",
"free function, documented in A.1 under its own name",
),
("content", "`ConceptUpsert` builder setter"),
("embedding_model", "`ConceptUpsert` builder setter"),
("valid_from", "builder setter"),
("valid_to", "builder setter"),
("retired", "`ConceptUpsert` builder setter"),
("normalized", "builder finaliser, described in prose"),
("chunk_rows", "module, not a method"),
];
let exempt: BTreeSet<&str> = EXEMPT.iter().map(|(n, _)| *n).collect();
let missing: Vec<String> = public_database_methods()
.into_iter()
.filter(|m| !exempt.contains(m.as_str()))
.filter(|m| !APPENDIX_A.contains(m.as_str()))
.collect();
assert!(
missing.is_empty(),
"Appendix A is normative and does not mention: {missing:?}\n\
Add them to A.1, or add an entry to EXEMPT saying why they do not \
belong in the public surface."
);
}
const REGISTER: &str = include_str!("../docs/architecture/s13-decision-register.md");
fn closure_markers(version: &str) -> [String; 2] {
[
format!("DELIVERED in {version}"),
format!("RESCHEDULED from {version}"),
]
}
const SCHEDULING: &[&str] = &[
"scheduled for",
"scheduled in",
"deferred to",
"deferred until",
"revisit at",
"revisited at",
"revisit in",
];
struct Claim {
entry: String,
phrase: String,
version: String,
at: usize,
}
fn semver(text: &str) -> Option<(u32, u32, u32)> {
let mut parts = text.split('.');
let major = parts.next()?.parse().ok()?;
let minor = parts.next()?.parse().ok()?;
let patch = match parts.next() {
Some(p) => p.parse().ok()?,
None => 0,
};
if parts.next().is_some() {
return None;
}
Some((major, minor, patch))
}
fn version_at(rest: &str) -> Option<String> {
let rest = rest.trim_start();
let rest = rest.strip_prefix('`').unwrap_or(rest);
let rest = rest.strip_prefix('v').unwrap_or(rest);
let token: String = rest
.chars()
.take_while(|c| c.is_ascii_digit() || *c == '.')
.collect();
let token = token.trim_end_matches('.').to_string();
if token.contains('.') && semver(&token).is_some() {
Some(token)
} else {
None
}
}
fn entry_containing(text: &str, offset: usize) -> String {
match text[..offset].rmatch_indices("<a id=\"d-").next() {
Some((i, m)) => {
let id: String = text[i + m.len()..]
.chars()
.take_while(|c| *c != '"')
.collect();
format!("D-{id}")
}
None => "(no enclosing decision entry)".to_string(),
}
}
fn entry_body(text: &str, offset: usize) -> &str {
let start = text[..offset]
.rmatch_indices("<a id=\"d-")
.next()
.map(|(i, _)| i)
.unwrap_or(0);
let end = text[start + 1..]
.find("<a id=\"d-")
.map(|i| start + 1 + i)
.unwrap_or(text.len());
&text[start..end]
}
fn is_quoted(text: &str, at: usize) -> bool {
matches!(
text[..at].chars().last(),
Some('"') | Some('\'') | Some('\u{201c}') | Some('\u{2018}')
)
}
fn scheduling_claims(text: &str) -> Vec<Claim> {
let lower = text.to_lowercase();
let mut found: Vec<Claim> = SCHEDULING
.iter()
.flat_map(|phrase| {
lower.match_indices(phrase).filter_map(move |(at, _)| {
if is_quoted(text, at) {
return None;
}
version_at(&text[at + phrase.len()..]).map(|version| Claim {
entry: entry_containing(text, at),
phrase: (*phrase).to_string(),
version,
at,
})
})
})
.collect();
found.sort_by_key(|c| c.at);
found
}
#[test]
fn no_decision_still_awaits_a_release_that_has_shipped() {
let current =
semver(env!("CARGO_PKG_VERSION")).expect("CARGO_PKG_VERSION is not a semver triple");
let overdue: Vec<String> = scheduling_claims(REGISTER)
.into_iter()
.filter(|c| semver(&c.version).is_some_and(|v| v <= current))
.filter(|c| {
let body = entry_body(REGISTER, c.at);
!closure_markers(&c.version).iter().any(|m| body.contains(m))
})
.map(|c| format!("{}: \"{} {}\"", c.entry, c.phrase, c.version))
.collect();
assert!(
overdue.is_empty(),
"current version is {}, and these decisions are still waiting for a \
release that has already shipped:\n {}\n\n\
This is exactly how D-087 and D-089 sat wrong through the whole of \
0.7.0 with a green suite. Do one of two things, and not a third:\n\
\x20 - it shipped: write `DELIVERED in <that version>` into the entry;\n\
\x20 - it did not ship: write `RESCHEDULED from <that version>`, and say \
which release it waits for now.\n\
Leave the original sentence exactly as it stands. It was true when it was \
written, and a deferral that leaves no trace is the failure this test \
exists to catch.",
env!("CARGO_PKG_VERSION"),
overdue.join("\n "),
);
}
#[test]
fn the_schedule_pattern_reads_versions_and_not_prose() {
for text in [
"Scheduled for 0.7.0 alongside the other schema work.",
"scheduled for `0.9.0` with the API break named.",
"Deferred to 1.0 because the surface freezes there.",
"revisit at v0.8.0 once the measurement exists.",
] {
assert_eq!(
scheduling_claims(text).len(),
1,
"should have found a scheduling claim in: {text}"
);
}
for text in [
"the integer-index rewrite of `Subgraph` is deferred until something measures it",
"`rowid_pk` on `concepts` is deferred to the release that implements erasure",
"deferred to 2 releases later",
"Scheduled for the next cycle.",
"D-087 and D-089 both read *\"Scheduled for 0.7.0\"*, and nothing went red.",
] {
assert!(
scheduling_claims(text).is_empty(),
"should not have fired on prose: {text}"
);
}
}
#[test]
fn the_delivery_marker_is_what_settles_an_overdue_entry() {
let settled = |text: &str, version: &str| {
let claim = &scheduling_claims(text)[0];
let body = entry_body(text, claim.at);
closure_markers(version).iter().any(|m| body.contains(m))
};
let bare = "<a id=\"d-999\"></a>D-999 - a thing. Scheduled for 0.1.0.\n";
assert_eq!(scheduling_claims(bare)[0].entry, "D-999");
assert!(!settled(bare, "0.1.0"), "an unmarked entry is overdue");
let shipped = "<a id=\"d-999\"></a>D-999 - a thing. Scheduled for 0.1.0. \
Prose in between. **DELIVERED in 0.1.0.**\n";
assert!(
settled(shipped, "0.1.0"),
"the marker must be found anywhere in the entry, not only beside the phrase"
);
let missed = "<a id=\"d-999\"></a>D-999 - a thing. Scheduled for 0.1.0. \
**RESCHEDULED from 0.1.0** to 0.2.0.\n";
assert!(
settled(missed, "0.1.0"),
"a missed release is closed out too"
);
assert!(
!settled(missed, "0.2.0"),
"a marker naming one release must not settle a claim on another"
);
}
const QUICKREF: &str = include_str!("../docs/quickref.md");
const SUBGRAPH_RS: &str = include_str!("../src/graph/subgraph.rs");
const PRIVATE_FIELD_TYPES: &[&str] = &["Subgraph", "NodeData", "EdgeRef"];
fn documented_struct_body<'a>(doc: &'a str, name: &str) -> Option<&'a str> {
let header = format!("pub struct {name} ");
let at = doc
.find(&header)
.or_else(|| doc.find(&format!("pub struct {name} {{")))?;
let open = at + doc[at..].find('{')?;
let mut depth = 0usize;
for (i, c) in doc[open..].char_indices() {
match c {
'{' => depth += 1,
'}' => {
depth -= 1;
if depth == 0 {
return Some(&doc[open + 1..open + i]);
}
}
_ => {}
}
}
None
}
fn public_methods_of(source: &str, header: &str) -> BTreeSet<String> {
block_after(source, header)
.lines()
.filter_map(|l| l.trim().strip_prefix("pub "))
.map(|l| l.trim_start_matches("async ").trim_start_matches("fn "))
.filter_map(|l| l.split(['(', '<', ' ']).next())
.filter(|n| !n.is_empty())
.map(str::to_string)
.collect()
}
#[test]
fn no_document_still_advertises_a_field_b1_made_private() {
let leaked: Vec<String> = PRIVATE_FIELD_TYPES
.iter()
.filter_map(|name| {
let body = documented_struct_body(QUICKREF, name)?;
body.contains("pub ")
.then(|| format!("{name} {{{}}}", body.trim()))
})
.collect();
assert!(
leaked.is_empty(),
"docs/quickref.md still declares public fields on types B1 made \
private:\n {}\n\
The accessors are the surface now. A document that reproduces a \
declaration has the same failure mode as a copied error enum: nobody \
executes it.",
leaked.join("\n ")
);
for name in PRIVATE_FIELD_TYPES {
assert!(
documented_struct_body(QUICKREF, name).is_some(),
"docs/quickref.md no longer declares `{name}` — either it was \
dropped from the document, or this test is looking for the wrong \
thing and is now passing vacuously"
);
}
}
#[test]
fn the_quoted_subgraph_surface_lists_every_public_method() {
let declared = public_methods_of(SUBGRAPH_RS, "impl Subgraph {");
assert!(
declared.len() > 10,
"parsed only {} methods off `impl Subgraph`, which means this test is \
reading the wrong thing rather than that the type shrank: {declared:?}",
declared.len()
);
let missing: Vec<&String> = declared
.iter()
.filter(|m| !QUICKREF.contains(m.as_str()))
.collect();
assert!(
missing.is_empty(),
"`impl Subgraph` has public methods that docs/quickref.md does not \
mention: {missing:?}\n\
quickref reproduces this surface verbatim, so an accessor added \
without it is a document that describes a smaller type than the crate \
has."
);
}
use std::collections::BTreeMap;
const README_MD: &str = include_str!("../README.md");
fn constants_by_file() -> BTreeMap<String, BTreeSet<String>> {
fn walk(
dir: &std::path::Path,
root: &std::path::Path,
out: &mut BTreeMap<String, BTreeSet<String>>,
) {
for entry in std::fs::read_dir(dir).expect("src/ is readable") {
let path = entry.expect("readable entry").path();
if path.is_dir() {
walk(&path, root, out);
} else if path.extension().is_some_and(|e| e == "rs") {
let rel = path
.strip_prefix(root)
.expect("under the manifest dir")
.to_string_lossy()
.replace('\\', "/");
let text = std::fs::read_to_string(&path).expect("valid utf-8");
for line in text.lines() {
if let Some(name) = declared_const(line) {
out.entry(name).or_default().insert(rel.clone());
}
}
}
}
}
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let mut out = BTreeMap::new();
walk(&root.join("src"), root, &mut out);
out
}
fn declared_const(line: &str) -> Option<String> {
let rest = line
.trim_start()
.strip_prefix("pub ")
.unwrap_or(line.trim_start());
let rest = rest.strip_prefix("const ")?;
let name: String = rest
.chars()
.take_while(|c| c.is_alphanumeric() || *c == '_')
.collect();
let screaming = !name.is_empty()
&& name
.chars()
.all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')
&& name.chars().any(|c| c.is_ascii_uppercase());
screaming.then_some(name)
}
fn passages(doc: &str) -> Vec<String> {
let mut out = Vec::new();
let mut current = String::new();
for line in doc.lines() {
let t = line.trim();
if t.starts_with('|') {
if !current.trim().is_empty() {
out.push(std::mem::take(&mut current));
}
out.push(line.to_string());
} else if t.is_empty() {
if !current.trim().is_empty() {
out.push(std::mem::take(&mut current));
}
} else {
current.push_str(line);
current.push(' ');
}
}
if !current.trim().is_empty() {
out.push(current);
}
out
}
fn files_named(passage: &str) -> BTreeSet<String> {
let bytes: Vec<char> = passage.chars().collect();
let mut out = BTreeSet::new();
let mut i = 0usize;
while i < bytes.len() {
if bytes[i].is_alphanumeric() || bytes[i] == '_' || bytes[i] == '/' || bytes[i] == '.' {
let start = i;
while i < bytes.len()
&& (bytes[i].is_alphanumeric()
|| bytes[i] == '_'
|| bytes[i] == '/'
|| bytes[i] == '.')
{
i += 1;
}
let token: String = bytes[start..i].iter().collect();
if token.ends_with(".rs") {
out.insert(token);
}
} else {
i += 1;
}
}
out
}
fn constants_named(passage: &str) -> BTreeSet<String> {
let mut out = BTreeSet::new();
for (i, chunk) in passage.split('`').enumerate() {
if i % 2 == 1 && declared_const(&format!("const {chunk}:")).is_some() {
out.insert(chunk.to_string());
}
}
out
}
#[test]
fn a_passage_that_places_a_constant_names_the_file_it_is_in() {
let defined = constants_by_file();
let mut wrong = Vec::new();
const DOCS: [(&str, &str); 3] = [
("docs/quickref.md", QUICKREF),
("README.md", README_MD),
("docs/architecture/appendices.md", APPENDIX_A),
];
for (doc_name, doc) in DOCS {
for passage in passages(doc) {
let files = files_named(&passage);
if files.is_empty() {
continue; }
for name in constants_named(&passage) {
let Some(homes) = defined.get(&name) else {
continue; };
let credited = homes
.iter()
.any(|home| files.iter().any(|named| home.ends_with(named.as_str())));
if !credited {
wrong.push(format!(
" {doc_name}: `{name}` is declared in {} — the passage names only {}\n ...{}",
homes.iter().cloned().collect::<Vec<_>>().join(", "),
files.iter().cloned().collect::<Vec<_>>().join(", "),
passage.chars().take(160).collect::<String>().trim_end()
));
}
}
}
}
assert!(
wrong.is_empty(),
"{} constant(s) are put in a file they are not in:\n{}\n\n\
A passage naming a source file is making a claim about where something \
lives, and this is the claim `util/limits.rs` carried wrongly for five \
releases. Correct the passage, or stop naming a file in it.",
wrong.len(),
wrong.join("\n")
);
}
#[test]
fn every_public_database_method_has_a_doc_comment() {
const EXEMPT: &[&str] = &["raw", "shadow_step"];
let lines: Vec<&str> = CONNECTION_RS.lines().collect();
let mut undocumented = Vec::new();
let mut in_impl = false;
for (i, line) in lines.iter().enumerate() {
if line.starts_with("impl Database {") {
in_impl = true;
continue;
}
if in_impl && line.starts_with('}') {
in_impl = false;
continue;
}
if !in_impl {
continue;
}
let t = line.trim_start();
let Some(name) = ["pub async fn ", "pub fn "].iter().find_map(|p| {
t.strip_prefix(p).map(|rest| {
rest.chars()
.take_while(|c| c.is_alphanumeric() || *c == '_')
.collect::<String>()
})
}) else {
continue;
};
if name.is_empty() || EXEMPT.contains(&name.as_str()) {
continue;
}
let mut j = i;
while j > 0 {
let prev = lines[j - 1].trim_start();
if prev.starts_with("#[") || prev.starts_with("#!") {
j -= 1;
continue;
}
break;
}
if j == 0 || !lines[j - 1].trim_start().starts_with("///") {
undocumented.push(format!("{name} (line {})", i + 1));
}
}
assert!(
undocumented.is_empty(),
"public methods on `Database` with no rustdoc: {undocumented:?}\n\n\
A method that loses its doc comment does not merely become \
undocumented — if the line above it belongs to a *neighbouring* \
method, that method's summary in `cargo doc` is now a description of \
this one. That is how `rebuild_current` and `checkpoint` were both \
wrong from 0.12.13 to 0.12.24.\n\n\
Appendix A naming the method is not this check: being listed in a \
document and having a doc comment are different facts."
);
}