docs: the repository of its own: licences, CI, citations checked in form (P13 step 6)

spec/ and examples/ are CC BY 4.0, the code MIT OR Apache-2.0. The
verbatim-pin and rules.md-id checks need wanderbooks' vendored sources and
rules.md, so they move to wanderbooks; here a citation is checked in form.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
swaitsandClaude Fable 5.1 committed 2026-10-29 16:35:23 -06:00
1 parent 82b53945c9
commit fc4430c685
8 files changed
+678 -63

No files matched your search

-28
View File
@@ -1,28 +0,0 @@
//! The documentation bookspec's identifiers live in: `docs/spec/rules.md`
//! carries each stated rule's id and speaks bookspec's `CHOICE`.
use std::path::Path;
use bookspec_checker::rules;
/// `docs/spec/rules.md` carries every stated rule's id under its heading,
/// and no longer calls a choice a decision (P13 step 3).
#[test]
fn rules_md_carries_every_id() {
let text = std::fs::read_to_string(
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../docs/spec/rules.md"),
)
.expect("docs/spec/rules.md");
for rule in rules().iter().filter(|r| r.status.is_some()) {
assert!(
text.contains(&format!("**bookspec:** `{}`", rule.id)),
"B-{} carries {}",
rule.b,
rule.id
);
}
assert!(
!text.contains("DECISION"),
"rules.md speaks bookspec's CHOICE"
);
}
+9 -22
View File
@@ -11,10 +11,6 @@ fn spec_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../spec")
}
fn sources_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../docs/spec/sources")
}
/// A rule's section as the text gives it.
#[derive(Default, Debug)]
struct Section {
@@ -86,15 +82,6 @@ fn one<'a>(s: &'a Section, id: &str, field: &str) -> &'a str {
&v[0]
}
/// Whitespace and typographic quotes made plain, so a pin copied from a
/// wrapped or curly-quoted source still matches.
fn squash(s: &str) -> String {
let plain = s
.replace(['\u{2019}', '\u{2018}'], "'")
.replace(['\u{201c}', '\u{201d}'], "\"");
plain.split_whitespace().collect::<Vec<_>>().join(" ")
}
fn backticked(s: &str) -> Vec<&str> {
s.split('`').skip(1).step_by(2).collect()
}
@@ -178,8 +165,9 @@ fn the_examples_are_the_catalogues() {
}
}
/// Every citation resolves: a vendored source exists, and a verbatim
/// pin appears in it; an external reference names its paragraph.
/// Every citation is well formed: a vendored source names its file and a
/// pin (checked verbatim by the consumer); an external reference names its
/// paragraph.
#[test]
fn every_citation_resolves() {
let sections = sections();
@@ -203,15 +191,14 @@ fn every_citation_resolves() {
let (file, rest) = rest
.split_once("`:")
.unwrap_or_else(|| panic!("{id}: {item}"));
let path = sources_dir().join(file.strip_prefix("sources/").unwrap_or(file));
let text = std::fs::read_to_string(&path)
.unwrap_or_else(|_| panic!("{id}: {file} is not vendored"));
let hay = squash(&text);
// Every quoted pin is verbatim in the file.
// The vendored texts live with the consumer (wanderbooks'
// `docs/spec/sources`), which checks each pin verbatim;
// here a citation names its file and quotes a pin.
assert!(file.ends_with(".md"), "{id}: {file}");
let quoted: Vec<&str> = rest.split('"').skip(1).step_by(2).collect();
for q in &quoted {
if !hay.contains(&squash(q)) {
bad.push(format!("{id}: {q:?} is not in {file}"));
if q.trim().is_empty() {
bad.push(format!("{id}: an empty pin in {file}"));
}
}
if quoted.is_empty() {