Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

xtask/tests/readme_building.rs

//! **The README's build line works where the README says it does** (OX-VARIANTS
//! 2026-10-28: "make sure ordinary cargo build works for the variants in the docs").
//!
//! The root workspace's members take some crates by path from sibling checkouts
//! (`../../../edda/...`), so `git clone monetize && cargo build -p monetize-server`
//! alone fails ("failed to read `../edda/crates/guest-host/Cargo.toml`", measured in a
//! lone clone on oden). The `## Building` section must name every sibling checkout the
//! root members reach, so the documented line is the one that builds. Zero
//! dependencies (the xtask rule): the manifests are read as text.

use std::collections::BTreeSet;
use std::path::{Component, Path, PathBuf};

fn root() -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR")).parent().expect("xtask/ has a parent").to_path_buf()
}

fn read(p: &Path) -> String {
    std::fs::read_to_string(p).unwrap_or_else(|e| panic!("read {}: {e}", p.display()))
}

/// The quoted strings of the root manifest's `members = [ ... ]`.
fn members(manifest: &str) -> Vec<String> {
    let start = manifest.find("members = [").expect("the root manifest lists its members");
    let body = &manifest[start + "members = [".len()..];
    let body = &body[..body.find(']').expect("the member list ends")];
    body.lines()
        .map(|l| l.split('#').next().unwrap_or(""))
        .flat_map(|l| l.split('"').skip(1).step_by(2).map(str::to_string).collect::<Vec<_>>())
        .collect()
}

/// The first directory outside the repository that `dir/<rel>` lands in, if any.
fn sibling_of(member_depth: usize, rel: &str) -> Option<String> {
    let mut depth = member_depth as isize;
    for c in Path::new(rel).components() {
        match c {
            Component::ParentDir => depth -= 1,
            Component::Normal(n) => {
                if depth < 0 {
                    return Some(n.to_string_lossy().into_owned());
                }
                depth += 1;
            }
            _ => {}
        }
    }
    None
}

#[test]
fn the_building_section_names_every_sibling_the_root_members_reach() {
    let root = root();
    let mut siblings = BTreeSet::new();
    for m in members(&read(&root.join("Cargo.toml"))) {
        let text = read(&root.join(&m).join("Cargo.toml"));
        let depth = Path::new(&m).components().count();
        for line in text.lines().map(|l| l.split('#').next().unwrap_or("")) {
            for part in line.split("path = \"").skip(1) {
                let rel = &part[..part.find('"').unwrap_or(part.len())];
                if let Some(s) = sibling_of(depth, rel) {
                    siblings.insert(s);
                }
            }
        }
    }
    assert!(!siblings.is_empty(), "the scan found no sibling path; if the root really builds alone now, say so in the README and delete this test");
    let readme = read(&root.join("README.md"));
    let building = readme.split("## Building").nth(1).expect("README.md has a `## Building` section");
    let building = &building[..building.find("\n## ").unwrap_or(building.len())];
    let missing: Vec<&String> = siblings.iter().filter(|s| !building.contains(&format!("../{s}"))).collect();
    assert!(
        missing.is_empty(),
        "README.md `## Building` does not name the sibling checkouts {missing:?} that the root members reach by path (all: {siblings:?})"
    );
}