use std::path::{Path, PathBuf};
fn repo_root() -> Result<PathBuf, String> {
let manifest = Path::new(env!("CARGO_MANIFEST_DIR"));
manifest
.parent()
.and_then(Path::parent)
.map(Path::to_path_buf)
.ok_or_else(|| {
format!(
"could not resolve the repo root from {}",
manifest.display()
)
})
}
fn read(root: &Path, relative: &str) -> Result<String, String> {
let path = root.join(relative);
std::fs::read_to_string(&path)
.map_err(|error| format!("failed to read {}: {error}", path.display()))
}
fn workspace_version(root: &Path) -> Result<String, String> {
let manifest = read(root, "Cargo.toml")?;
let version = manifest
.lines()
.find_map(|line| line.trim().strip_prefix("version = \""))
.and_then(|rest| rest.split('"').next())
.ok_or_else(|| "no top-level `version =` in the workspace manifest".to_owned())?;
Ok(version.to_owned())
}
fn series(version: &str) -> &str {
let Some((major, rest)) = version.split_once('.') else {
return version;
};
rest.find('.').map_or(version, |offset| {
let second_dot = major.len().saturating_add(1).saturating_add(offset);
version.get(..second_dot).unwrap_or(version)
})
}
#[test]
fn a_series_is_the_major_and_minor() {
assert_eq!(series("0.4"), "0.4");
assert_eq!(series("0.4.1"), "0.4");
assert_eq!(series("0.4.13"), "0.4");
assert_eq!(series("1.0.0"), "1.0");
assert_eq!(series("10.20.3"), "10.20");
assert_eq!(series("4"), "4");
}
#[test]
fn the_readme_install_snippets_name_the_published_version() -> Result<(), String> {
let root = repo_root()?;
let version = workspace_version(&root)?;
let readme = read(&root, "README.md")?;
let mut checked = 0_usize;
for (number, line) in readme.lines().enumerate() {
let trimmed = line.trim();
if !trimmed.starts_with("protocol-droid = ") {
continue;
}
checked += 1;
let after_key = trimmed
.trim_start_matches("protocol-droid = ")
.trim_start_matches('{')
.trim_start();
let after_key = after_key
.strip_prefix("version = ")
.unwrap_or(after_key)
.trim_start();
let declared = after_key
.strip_prefix('"')
.and_then(|rest| rest.split_once('"'))
.map(|(version, _)| version)
.ok_or_else(|| {
format!(
"README.md:{}: could not read a version out of `{trimmed}`",
number + 1
)
})?;
assert_eq!(
series(declared),
series(&version),
"README.md:{} says `protocol-droid = \"{declared}\"` but the workspace is {version}. \
A reader who copies this installs a crate that predates the documented feature set.",
number + 1
);
}
assert!(
checked >= 2,
"expected the README to carry at least two install snippets, found {checked}. \
Either they were removed or the scan above no longer recognises their shape."
);
Ok(())
}
#[test]
fn the_readme_feature_table_matches_the_manifest() -> Result<(), String> {
let root = repo_root()?;
let manifest = read(&root, "crates/protocol-droid/Cargo.toml")?;
let readme = read(&root, "README.md")?;
let mut in_features = false;
for line in manifest.lines() {
if line.starts_with('[') {
in_features = line.trim() == "[features]";
continue;
}
if !in_features {
continue;
}
let Some((name, _)) = line.split_once('=') else {
continue;
};
let name = name.trim();
if name.is_empty()
|| name == "default"
|| !name
.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
{
continue;
}
assert!(
readme.contains(&format!("| `{name}`")),
"feature `{name}` is declared in the manifest but has no row in the README's feature \
table, so nobody can discover it"
);
}
Ok(())
}
#[test]
fn the_site_security_snippet_matches_the_compiled_example() -> Result<(), String> {
let root = repo_root()?;
let example = read(
&root,
"crates/protocol-droid/examples/oauth_resource_server.rs",
)?;
let site = read(&root, "site/src/content/docs/guides/security.md")?;
let mut region: Vec<&str> = Vec::new();
let mut inside = false;
for line in example.lines() {
let trimmed = line.trim_start();
if trimmed.starts_with("// ANCHOR: assembly") {
inside = true;
continue;
}
if trimmed.starts_with("// ANCHOR_END: assembly") {
inside = false;
continue;
}
if inside {
region.push(line);
}
}
while region.first().is_some_and(|line| line.trim().is_empty()) {
region.remove(0);
}
while region.last().is_some_and(|line| line.trim().is_empty()) {
region.pop();
}
assert!(
region.len() > 10,
"the example's `assembly` anchor captured only {} lines; the markers probably moved",
region.len()
);
let mut after_fence = site.lines().skip_while(|line| line.trim() != "```rust");
after_fence.next();
let published: Vec<&str> = after_fence
.take_while(|line| line.trim() != "```")
.collect();
let mut cursor = 0_usize;
for line in ®ion {
if line.trim().is_empty() {
continue;
}
let remaining = published.get(cursor..).ok_or_else(|| {
format!("the search cursor ran {cursor} lines past the end of the published fence")
})?;
match remaining
.iter()
.position(|candidate| candidate.trim() == line.trim())
{
Some(offset) => cursor += offset + 1,
None => {
return Err(format!(
"site/src/content/docs/guides/security.md no longer carries this line from the \
example, in order: `{line}`.\nRe-splice the example's `assembly` region into \
the guide."
));
}
}
}
Ok(())
}
#[test]
fn site_is_the_only_document_tree() -> Result<(), String> {
let root = repo_root()?;
for relative in ["docs/guide", "book.toml"] {
let path = root.join(relative);
if path.exists() {
return Err(format!(
"{} exists again. The site under `site/` is the only maintained document tree: \
no workflow builds this, the deploy script does not publish it, and it drifts \
from the site without failing anything. Either delete it or move its content \
into the site — do not leave two.",
path.display()
));
}
}
Ok(())
}