use areev_cal::parser::parse;
const REFERENCE: &str = include_str!("../../../docs/cal-reference.md");
const REAL_HASH: &str =
"sha256:684c6c9bda818630a870119d0726e4d242ed537af061658ef6f3acb158a2c67d";
fn sql_fences(md: &str) -> Vec<(usize, String)> {
let mut out = Vec::new();
let mut current: Option<(usize, Vec<&str>)> = None;
for (i, line) in md.lines().enumerate() {
match (&mut current, line.trim_end()) {
(None, "```sql") => current = Some((i + 2, Vec::new())),
(Some((start, body)), "```") => {
out.push((*start, body.join("\n")));
let _ = start;
current = None;
}
(Some((_, body)), l) => body.push(l),
_ => {}
}
}
out
}
fn examples(fence: &str) -> Vec<String> {
fence
.split("\n\n")
.map(|chunk| {
chunk
.lines()
.filter(|l| !l.trim_start().starts_with("--"))
.collect::<Vec<_>>()
.join("\n")
})
.map(|q| q.replace("sha256:<hash>", REAL_HASH).replace("sha256:a1b2c3d4...", REAL_HASH))
.filter(|q| !q.trim().is_empty())
.flat_map(|chunk| split_statements(&chunk))
.collect()
}
fn split_statements(chunk: &str) -> Vec<String> {
let lines: Vec<&str> = chunk.lines().filter(|l| !l.trim().is_empty()).collect();
if lines.len() > 1 && lines.iter().all(|l| parse(l).is_ok()) {
return lines.into_iter().map(str::to_string).collect();
}
vec![chunk.to_string()]
}
#[test]
fn every_sql_example_in_the_cal_reference_parses() {
let fences = sql_fences(REFERENCE);
assert!(
fences.len() > 10,
"extracted only {} sql fences — the extractor is broken, not the docs",
fences.len()
);
let mut failures = Vec::new();
let mut checked = 0usize;
for (line, fence) in &fences {
for q in examples(fence) {
checked += 1;
if let Err(e) = parse(&q) {
failures.push(format!(
"docs/cal-reference.md:{line}\n query: {}\n error: {e}",
q.replace('\n', "\n ")
));
}
}
}
assert!(
checked > 30,
"only {checked} examples extracted from {} fences",
fences.len()
);
assert!(
failures.is_empty(),
"{} of {checked} documented CAL examples do not parse:\n\n{}",
failures.len(),
failures.join("\n\n")
);
}
#[test]
fn the_pipeline_stage_table_matches_the_grammar() {
let err = parse(r#"RECALL facts WHERE subject = "john" | WHERE relation = "prefers""#)
.expect_err("`| WHERE` is not a pipeline stage");
let msg = err.to_string();
let accepted = msg
.split_once('(')
.and_then(|(_, rest)| rest.split_once(')'))
.map(|(list, _)| list.to_string())
.unwrap_or_else(|| panic!("parser error no longer lists the stages: {msg}"));
let table = REFERENCE
.lines()
.filter(|l| l.starts_with("| `\\| "))
.flat_map(|l| {
l.trim_start_matches("| `\\| ")
.split('`')
.next()
.map(|s| s.trim().to_string())
})
.collect::<Vec<_>>();
assert!(!table.is_empty(), "no pipeline stage rows found in §4");
for row in &table {
let keyword = row.split([' ', '`']).next().unwrap_or(row);
assert!(
accepted.contains(keyword),
"§4 documents a `| {row}` stage, but the parser does not accept it \
(it accepts: {accepted})"
);
}
}