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

nordisk/holger

xtask/src/fmt.rs

//! `cargo xtask fmt [--check|--write]` (lane OSSF-SILVER-CODE, 2026-10-31): rustfmt over
//! the WHOLE tree, every `.rs` file git tracks, with the repository's `rustfmt.toml`.
//!
//! `--check` (the default) lists the files rustfmt would change and exits 1 when there
//! is one; `--write` formats them. `cargo xtask ci --quick` runs the check on every
//! push, so the tree stays formatted (OpenSSF silver: coding_standards_enforced).
//!
//! Why not `cargo fmt --all`: it follows local path dependencies OUT of this repository
//! (its own `--help` says so), so in an estate checkout it would reformat the siblings,
//! and it misses the files of the detached workspaces (estate, holger-downstream, fuzz,
//! ...). `git ls-files` names exactly this repository's files, in every workspace.
//!
//! Generated code (`*/generated/*.rs`, written by `cargo xtask gen-proto` and pulled in
//! with `include!`) is left as its generator writes it: [`SKIP`].

use std::path::{Path, PathBuf};
use std::process::Command;

/// Path fragments of tracked `.rs` files that are not ours to format.
pub const SKIP: &[&str] = &["/generated/"];

/// Files per rustfmt run: keeps the argument list well under any `ARG_MAX`.
const BATCH: usize = 200;

/// Whether a repository-relative path is formatted by this verb.
pub fn keep(rel: &str) -> bool {
    let rooted = format!("/{rel}");
    rel.ends_with(".rs") && !SKIP.iter().any(|s| rooted.contains(s))
}

/// Every `.rs` file git tracks under `root`, minus [`SKIP`], as absolute paths.
pub fn tracked_rs(root: &Path) -> anyhow::Result<Vec<PathBuf>> {
    let out = Command::new("git")
        .args(["ls-files", "-z", "--", "*.rs"])
        .current_dir(root)
        .output()
        .map_err(|e| anyhow::anyhow!("could not run git ls-files in {}: {e}", root.display()))?;
    anyhow::ensure!(
        out.status.success(),
        "git ls-files in {} failed: {}",
        root.display(),
        String::from_utf8_lossy(&out.stderr)
    );
    Ok(out
        .stdout
        .split(|b| *b == 0)
        .filter(|s| !s.is_empty())
        .map(|s| String::from_utf8_lossy(s).into_owned())
        .filter(|rel| keep(rel))
        .map(|rel| root.join(rel))
        .collect())
}

/// rustfmt over `files` with `config` (a `rustfmt.toml`). With `write` false it changes
/// nothing and answers the files rustfmt would change; with `write` true it formats
/// them and answers the files it changed. A file rustfmt cannot parse is an error.
pub fn rustfmt(config: &Path, files: &[PathBuf], write: bool) -> anyhow::Result<Vec<PathBuf>> {
    let bin = std::env::var_os("RUSTFMT").unwrap_or_else(|| "rustfmt".into());
    let mut changed = Vec::new();
    for batch in files.chunks(BATCH) {
        let mut cmd = Command::new(&bin);
        cmd.arg("--config-path").arg(config).arg("-l");
        if !write {
            cmd.arg("--check");
        }
        let out = cmd.args(batch).output().map_err(|e| {
            anyhow::anyhow!("could not run rustfmt ({}): {e}", bin.to_string_lossy())
        })?;
        let stderr = String::from_utf8_lossy(&out.stderr);
        anyhow::ensure!(
            !stderr.lines().any(|l| l.starts_with("error")),
            "rustfmt could not format every file:\n{stderr}"
        );
        // `--check -l` exits 1 when a file differs; that is the answer, not a failure.
        anyhow::ensure!(
            out.status.success() || !write,
            "rustfmt failed ({}):\n{stderr}",
            out.status
        );
        for line in String::from_utf8_lossy(&out.stdout).lines() {
            let p = PathBuf::from(line.trim());
            if !p.as_os_str().is_empty() && !changed.contains(&p) {
                changed.push(p);
            }
        }
    }
    Ok(changed)
}

/// The verb: `--write` formats, anything else checks.
pub fn run(root: &Path, args: &[String]) -> i32 {
    let write = args.iter().any(|a| a == "--write");
    let files = match tracked_rs(root) {
        Ok(f) => f,
        Err(e) => {
            eprintln!("xtask fmt: {e:#}");
            return 2;
        }
    };
    match rustfmt(&root.join("rustfmt.toml"), &files, write) {
        Ok(changed) if write => {
            eprintln!(
                "xtask fmt: {} of {} files formatted",
                changed.len(),
                files.len()
            );
            0
        }
        Ok(changed) if changed.is_empty() => {
            eprintln!("xtask fmt: {} files, all rustfmt-clean", files.len());
            0
        }
        Ok(changed) => {
            for f in &changed {
                println!("{}", f.strip_prefix(root).unwrap_or(f).display());
            }
            eprintln!(
                "xtask fmt: {} of {} files are not rustfmt-clean; `cargo xtask fmt --write` formats them",
                changed.len(),
                files.len()
            );
            1
        }
        Err(e) => {
            eprintln!("xtask fmt: {e:#}");
            2
        }
    }
}

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

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

    #[test]
    fn generated_code_is_left_as_its_generator_writes_it() {
        assert!(!keep("server/lib/src/generated/holger.v1.rs"));
        assert!(!keep("holger-cli/src/generated/holger.room.v1.rs"));
        assert!(keep("server/lib/src/lib.rs"));
        assert!(keep("generated.rs"), "a file NAMED generated is ours");
        assert!(!keep("README.md"));
    }

    #[test]
    fn the_walk_names_this_repositorys_files_in_every_workspace() {
        let files = tracked_rs(&root()).expect("git ls-files");
        let has = |rel: &str| files.iter().any(|f| f.ends_with(rel));
        assert!(
            has("xtask/src/fmt.rs"),
            "the xtask itself (the estate workspace)"
        );
        assert!(has("server/lib/src/lib.rs"), "the root workspace");
        assert!(!files
            .iter()
            .any(|f| f.to_string_lossy().contains("/generated/")));
    }

    /// Measure, never assert: the check must see an unformatted file, and must not touch
    /// it, before a green answer over the tree means anything.
    #[test]
    fn the_check_sees_an_unformatted_file_and_write_formats_it() {
        let dir = std::env::temp_dir().join(format!("holger-xtask-fmt-{}", std::process::id()));
        std::fs::create_dir_all(&dir).unwrap();
        let config = dir.join("rustfmt.toml");
        std::fs::write(&config, "edition = \"2021\"\n").unwrap();
        let ugly = dir.join("ugly.rs");
        let text = "fn  main( ){let x=1;println!(\"{}\",x);}\n";
        std::fs::write(&ugly, text).unwrap();
        let tidy = dir.join("tidy.rs");
        std::fs::write(&tidy, "fn main() {}\n").unwrap();

        let files = vec![ugly.clone(), tidy.clone()];
        let seen = rustfmt(&config, &files, false).expect("rustfmt --check");
        assert_eq!(
            seen,
            vec![ugly.clone()],
            "the check names the unformatted file only"
        );
        assert_eq!(
            std::fs::read_to_string(&ugly).unwrap(),
            text,
            "the check changes nothing"
        );

        rustfmt(&config, &files, true).expect("rustfmt");
        assert!(
            rustfmt(&config, &files, false)
                .expect("rustfmt --check")
                .is_empty(),
            "formatted after --write"
        );
        std::fs::remove_dir_all(&dir).ok();
    }

    #[test]
    fn a_file_rustfmt_cannot_parse_is_an_error_not_a_pass() {
        let dir = std::env::temp_dir().join(format!("holger-xtask-fmt-bad-{}", std::process::id()));
        std::fs::create_dir_all(&dir).unwrap();
        let config = dir.join("rustfmt.toml");
        std::fs::write(&config, "edition = \"2021\"\n").unwrap();
        let bad = dir.join("bad.rs");
        std::fs::write(&bad, "fn main( {\n").unwrap();
        assert!(rustfmt(&config, &[bad], false).is_err());
        std::fs::remove_dir_all(&dir).ok();
    }

    /// The silver criterion itself: Holger's whole tree is rustfmt-clean.
    #[test]
    fn holgers_whole_tree_is_rustfmt_clean() {
        let root = root();
        let files = tracked_rs(&root).expect("git ls-files");
        let changed = rustfmt(&root.join("rustfmt.toml"), &files, false).expect("rustfmt --check");
        let shown: Vec<String> = changed
            .iter()
            .take(20)
            .map(|f| f.strip_prefix(&root).unwrap_or(f).display().to_string())
            .collect();
        assert!(
            changed.is_empty(),
            "{} of {} files are not rustfmt-clean (`cargo xtask fmt --write`), first ones:\n{}",
            changed.len(),
            files.len(),
            shown.join("\n")
        );
    }
}