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

nordisk/znippy-zoomies

tests/law_common/mod.rs

//! **The workspace member scan the three law guards share.**
//!
//! ## Why this file exists
//!
//! `tests/rayon_free_law.rs`, `tests/bench_target_law.rs` and
//! `tests/required_features_law.rs` each carried their OWN copy of the same twelve
//! lines, and each copy said, in a doc comment, that a newly added crate is covered
//! "automatically (no carve-outs)". All four copies read the member list like this:
//!
//! ```ignore
//! manifest.lines().find(|l| l.trim_start().starts_with("members"))
//! ```
//!
//! One LINE. `members` is a TOML array, and an array may wrap — `cargo add`, a
//! `taplo fmt`, or a human adding an eleventh crate past the 100-column mark all
//! produce the wrapped form, and znippy's own root manifest is already written that
//! way. The moment this one does, `find` returns the line holding `members = [`, the
//! `line.find(']')` on it returns `None`, the `if let` falls through, and every guard
//! quietly narrows to the ROOT crate while still reporting green over a workspace it
//! has stopped looking at. Nothing fails. Nothing warns. Ten crates leave the law.
//!
//! That is the same silent-narrowing defect the guards themselves were written to
//! catch, sitting in the guards' own discovery, in triplicate — and it had already
//! been found and fixed once on the `master` branch (`c4c5084`, "rayon-law members
//! parse was single-line — the scan silently shrank") without ever reaching `main`,
//! where all the work is.
//!
//! ## The fix is by construction, not another guard
//!
//! The parse now spans lines, and there is ONE copy of it (LAW 5). It also refuses
//! to return a short list quietly: a member directory with no `Cargo.toml` is a
//! panic, not a skip, because "the path was wrong" and "the crate is clean" must not
//! look the same to a law.

use std::path::PathBuf;

/// The workspace root, as a directory.
pub fn workspace_root() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}

/// Every `members = [...]` entry from the root manifest, in declaration order.
///
/// Spans lines: from the `members` key to the matching `]`, with `#` comments
/// stripped first so a commented-out crate is not counted and a `]` inside a comment
/// cannot end the array early.
pub fn member_names(manifest: &str) -> Vec<String> {
    // Strip `#` comments. No TOML string in a members array legitimately contains
    // one, and leaving them in lets a comment close the array.
    let stripped: String = manifest
        .lines()
        .map(|l| match l.find('#') {
            Some(i) => &l[..i],
            None => l,
        })
        .collect::<Vec<_>>()
        .join("\n");

    let Some(key) = stripped.find("members") else {
        return Vec::new();
    };
    let Some(open) = stripped[key..].find('[') else {
        return Vec::new();
    };
    let open = key + open;
    let Some(close) = stripped[open..].find(']') else {
        return Vec::new();
    };
    let close = open + close;

    stripped[open + 1..close]
        .split(',')
        .map(|s| s.trim().trim_matches('"').trim().to_string())
        .filter(|s| !s.is_empty())
        .collect()
}

/// The workspace root plus every member, as crate directories.
///
/// Panics if a declared member has no `Cargo.toml` — a law that scanned a path that
/// is not there would report the same green as one that scanned a clean crate.
pub fn crate_dirs() -> Vec<PathBuf> {
    let root = workspace_root();
    let manifest = std::fs::read_to_string(root.join("Cargo.toml")).expect("read root Cargo.toml");
    let names = member_names(&manifest);
    assert!(
        !names.is_empty(),
        "the root manifest declares NO workspace members — the member scan is empty, so \
         every law using it would pass over the root crate alone"
    );
    let mut dirs = vec![root.clone()];
    for name in &names {
        let dir = root.join(name);
        assert!(
            dir.join("Cargo.toml").is_file(),
            "member {name:?} is declared in the root manifest but {dir:?} has no Cargo.toml — \
             a law cannot tell a bad path from a clean crate"
        );
        dirs.push(dir);
    }
    dirs
}

/// Every member's `src/` tree, plus the root crate's.
pub fn workspace_src_roots() -> Vec<PathBuf> {
    crate_dirs().into_iter().map(|d| d.join("src")).collect()
}

// ---------------------------------------------------------------------------
// The scan's own guard. A discovery helper that silently returns a short list is
// exactly what this module exists to remove, so it is proved here rather than
// assumed.
// ---------------------------------------------------------------------------

#[cfg(test)]
mod scan_guard {
    use super::*;

    /// The old single-line reader, kept verbatim so the red is a MEASUREMENT of the
    /// defect rather than a story about it.
    fn single_line_reader(manifest: &str) -> Vec<String> {
        let mut out = Vec::new();
        if let Some(line) = manifest
            .lines()
            .find(|l| l.trim_start().starts_with("members"))
        {
            if let (Some(a), Some(b)) = (line.find('['), line.find(']')) {
                for raw in line[a + 1..b].split(',') {
                    let name = raw.trim().trim_matches('"');
                    if !name.is_empty() {
                        out.push(name.to_string());
                    }
                }
            }
        }
        out
    }

    const WRAPPED: &str = r#"
[workspace]
members = [
    "gatling",
    "lbzip2",
    "osm-katana",
]
resolver = "2"
"#;

    #[test]
    fn a_wrapped_members_array_is_read_whole_and_the_old_reader_read_none_of_it() {
        assert_eq!(
            member_names(WRAPPED),
            vec!["gatling", "lbzip2", "osm-katana"],
            "the wrapped form must parse to all three members"
        );
        // THE RED: the reader every law shipped saw ZERO of them, and said nothing.
        assert!(
            single_line_reader(WRAPPED).is_empty(),
            "the old single-line reader is supposed to MISS the wrapped form — if it \
             does not, this fixture no longer reproduces the defect and the guard above \
             is measuring nothing"
        );
    }

    #[test]
    fn the_inline_form_still_parses_and_both_readers_agree_on_it() {
        let inline = "[workspace]\nmembers = [\"a\", \"b\"]\nresolver = \"2\"\n";
        assert_eq!(member_names(inline), vec!["a", "b"]);
        assert_eq!(single_line_reader(inline), member_names(inline));
    }

    #[test]
    fn a_commented_out_member_is_not_a_member_and_a_hash_cannot_close_the_array() {
        let m = "[workspace]\nmembers = [\n  \"a\",\n  # \"gone\"],\n  \"b\",\n]\n";
        assert_eq!(
            member_names(m),
            vec!["a", "b"],
            "a commented member must not count, and its `]` must not end the array"
        );
    }

    #[test]
    fn this_workspace_resolves_every_declared_member_to_a_real_crate() {
        let dirs = crate_dirs();
        // Root + members. Pinning the floor, not the exact count, so adding a crate
        // does not edit a test — but a scan that collapsed to the root cannot pass.
        assert!(
            dirs.len() >= 8,
            "the workspace scan found only {} director(ies) — it has collapsed: {dirs:?}",
            dirs.len()
        );
    }
}