Skip to main content

split_modules/
verify.rs

1//! Compiler-based verification: locate the crate manifest and run `cargo check`.
2
3use std::path::{Path, PathBuf};
4use std::process::Command;
5
6/// Walk upward from `start` to find the directory containing `Cargo.toml`.
7pub fn find_manifest_dir(start: &Path) -> Option<PathBuf> {
8    let mut dir = if start.is_dir() {
9        Some(start.to_path_buf())
10    } else {
11        start.parent().map(|p| p.to_path_buf())
12    };
13    while let Some(d) = dir {
14        if d.join("Cargo.toml").is_file() {
15            return Some(d);
16        }
17        dir = d.parent().map(|p| p.to_path_buf());
18    }
19    None
20}
21
22/// Result of a `cargo check` run.
23pub struct CheckResult {
24    pub ok: bool,
25    pub stderr: String,
26}
27
28/// Run `cargo check` in `manifest_dir`. `all_targets` also checks tests/examples/benches.
29pub fn cargo_check(manifest_dir: &Path, all_targets: bool) -> CheckResult {
30    let mut cmd = Command::new("cargo");
31    cmd.arg("check").arg("--quiet").current_dir(manifest_dir);
32    if all_targets {
33        cmd.arg("--all-targets");
34    }
35    // Don't let a project-level `-D warnings` config turn cosmetic warnings into
36    // rollback triggers; we only care about whether it compiles.
37    cmd.env("RUSTFLAGS", "--cap-lints=warn");
38    match cmd.output() {
39        Ok(out) => CheckResult {
40            ok: out.status.success(),
41            stderr: String::from_utf8_lossy(&out.stderr).to_string(),
42        },
43        Err(e) => CheckResult {
44            ok: false,
45            stderr: format!("failed to run cargo check: {e}"),
46        },
47    }
48}
49
50/// Keep only the most relevant tail of a cargo error log for reporting.
51pub fn error_excerpt(stderr: &str) -> String {
52    let lines: Vec<&str> = stderr
53        .lines()
54        .filter(|l| {
55            let t = l.trim_start();
56            t.starts_with("error") || t.starts_with("-->") || t.contains("not found") || t.contains("cannot find")
57        })
58        .take(12)
59        .collect();
60    if lines.is_empty() {
61        stderr.lines().rev().take(8).collect::<Vec<_>>().into_iter().rev().collect::<Vec<_>>().join("\n")
62    } else {
63        lines.join("\n")
64    }
65}