use std::collections::BTreeSet;
const ABSENCE: &[&str] = &[
"is not implemented",
"are not implemented",
"not here is",
"no way to",
"is absent",
"are absent",
"does not exist",
"do not exist",
];
const PROSE: &[&str] = &[
"README.md",
"docs/functions.md",
"docs/3d.md",
"docs/scope.md",
"docs/routing.md",
"docs/wasm.md",
"docs/quickstart.md",
"src/functions/threed.rs",
"src/functions/threed_metric.rs",
"src/functions/surface.rs",
"src/coords.rs",
"src/geom.rs",
];
const KNOWN_ABSENT: &[&str] = &[
"ST_Volume",
"ST_3DIntersection",
"ST_3DUnion",
"ST_3DDifference",
"ST_Extrude",
"ST_Tesselate",
"ST_MakeSolid",
"ST_IsSolid",
"ST_3DConvexHull",
"ST_ApproximateMedialAxis",
"ST_MinkowskiSum",
"ST_StraightSkeleton",
"ST_ConstrainedDelaunayTriangles",
"ST_Dump",
"ST_DumpPoints",
"ST_DumpRings",
"ST_OffsetCurve",
"ST_ClusterDBSCAN",
"ST_ClusterKMeans",
"ST_CurveToLine",
"ST_HasArc",
"ST_LineToCurve",
"ST_IsValidDetail",
"ST_AsFlatGeobuf",
"ST_AsGeobuf",
"ST_FromFlatGeobuf",
"ST_GeogFromText",
"ST_AsWKB",
"ST_Collect",
"ST_3DLineInterpolatePoint",
"ST_Force3D",
];
fn registered() -> BTreeSet<String> {
let mut names = BTreeSet::new();
for e in kenro::functions::manifest::active_functions() {
names.insert(e.sql_name.to_string());
}
for e in kenro::functions::manifest::active_aggregates() {
names.insert(e.sql_name.to_string());
}
names
}
fn clauses(text: &str) -> Vec<String> {
text.replace('\n', " ")
.split(['.', ';', '|'])
.map(|c| c.split_whitespace().collect::<Vec<_>>().join(" "))
.collect()
}
fn names_in(clause: &str) -> Vec<String> {
let mut out = Vec::new();
let bytes = clause.as_bytes();
let mut i = 0;
while let Some(found) = clause[i..].find("ST_") {
let start = i + found;
let mut end = start + 3;
while end < bytes.len() && (bytes[end].is_ascii_alphanumeric() || bytes[end] == b'_') {
end += 1;
}
out.push(clause[start..end].to_string());
i = end.max(start + 3);
}
out
}
#[test]
fn no_document_claims_a_registered_function_is_missing() {
let reg = registered();
let allowed: BTreeSet<&str> = KNOWN_ABSENT.iter().copied().collect();
let mut findings = Vec::new();
for file in PROSE {
let text = match std::fs::read_to_string(file) {
Ok(t) => t,
Err(_) => continue, };
for clause in clauses(&text) {
if !ABSENCE.iter().any(|w| clause.contains(w)) {
continue;
}
for name in names_in(&clause) {
if reg.contains(&name) && !allowed.contains(name.as_str()) {
findings.push(format!(
"{file}: says {name} is missing, but it is registered\n \"{}\"",
clause.trim().chars().take(140).collect::<String>()
));
}
}
}
}
assert!(
findings.is_empty(),
"stale absence claims — rewrite the sentence, do not add an exemption:\n {}",
findings.join("\n ")
);
}
#[test]
fn the_known_absent_list_contains_nothing_that_exists() {
let reg = registered();
const EXPECTED_OVERLAP: &[&str] = &["ST_Force3D", "ST_3DLineInterpolatePoint"];
let wrong: Vec<&&str> = KNOWN_ABSENT
.iter()
.filter(|n| reg.contains(**n) && !EXPECTED_OVERLAP.contains(*n))
.collect();
assert!(
wrong.is_empty(),
"these are registered, so claiming they are absent is wrong wherever it \
happens — drop them from KNOWN_ABSENT and fix the prose: {wrong:?}"
);
}