use crate::cli::OutputFormat;
use crate::contracts;
use crate::error::CommandError;
use crate::process::{ProcessSpec, run, run_quiet};
use crate::project;
use crate::project::ProjectConfig;
use crate::tool::{HealthReport, HealthStatus, Tool, certification_status, probe};
pub(crate) fn execute(format: OutputFormat) -> Result<(), CommandError> {
let project = project::discover()?;
let mut report = HealthReport::default();
report.push(
"project",
HealthStatus::Ok,
project.root().display().to_string(),
);
report.push("frontend", HealthStatus::Ok, project.frontend.as_str());
let frontend_root = project.frontend_root();
for (label, exists) in [
("Cargo.toml", project.root().join("Cargo.toml").is_file()),
("package.json", frontend_root.join("package.json").is_file()),
(
"vite.config.ts",
frontend_root.join("vite.config.ts").is_file(),
),
(
"pnpm-lock.yaml",
frontend_root.join("pnpm-lock.yaml").is_file(),
),
] {
report.push(
label,
if exists {
HealthStatus::Ok
} else {
HealthStatus::Error
},
if exists { "present" } else { "missing" },
);
}
let tools_ok = [Tool::Cargo, Tool::Node, Tool::Pnpm]
.into_iter()
.all(|tool| match probe(tool) {
Ok(version) => {
report.push(
tool.program(),
certification_status(tool, version),
version.to_string(),
);
true
}
Err(error) => {
report.push(tool.program(), HealthStatus::Error, error.to_string());
false
}
});
if tools_ok && !report.has_error() {
match contracts::check(&project) {
Ok(artifact) => {
report.push(
"cross-stack linker",
HealthStatus::Ok,
"registered pages and props match",
);
match exposure_lint(&artifact) {
Ok(()) => report.push(
"exposure firewall",
HealthStatus::Ok,
"no secret-bearing field names in browser contracts",
),
Err(error) => report.push("exposure firewall", HealthStatus::Error, error),
}
}
Err(error) => report.push("cross-stack linker", HealthStatus::Error, error),
}
let typescript = ProcessSpec::new(Tool::Pnpm.executable(), project.frontend_root())
.args(["exec", "tsc", "--noEmit"]);
match run_for_format(&typescript, format) {
Ok(()) => report.push("typescript", HealthStatus::Ok, "type check passed"),
Err(error) => report.push("typescript", HealthStatus::Error, error.to_string()),
}
let cargo = ProcessSpec::new("cargo", project.root())
.arg("check")
.env("CARGO_TARGET_DIR", check_target_dir(&project));
match run_for_format(&cargo, format) {
Ok(()) => report.push("cargo", HealthStatus::Ok, "cargo check passed"),
Err(error) => report.push("cargo", HealthStatus::Error, error.to_string()),
}
}
match format {
OutputFormat::Human => report.print_human(),
OutputFormat::Json => report.print_json()?,
}
if report.has_error() {
Err(CommandError::Unhealthy("project"))
} else {
Ok(())
}
}
fn check_target_dir(project: &ProjectConfig) -> std::path::PathBuf {
use std::hash::{DefaultHasher, Hash, Hasher};
let mut hasher = DefaultHasher::new();
project.root().hash(&mut hasher);
std::env::temp_dir()
.join("arcature-check")
.join(format!("{:016x}", hasher.finish()))
}
fn exposure_lint(artifact: &contracts::Artifact) -> Result<(), String> {
let hits = contracts::dangerous_fields(&artifact.pages);
if hits.is_empty() {
return Ok(());
}
let listed = hits
.iter()
.map(|hit| format!("`{}`.`{}`", hit.page, hit.field))
.collect::<Vec<_>>()
.join(", ");
Err(format!(
"secret-bearing field names in browser contracts: {listed} (field-name linting is \
defense-in-depth; the boundary is the explicit ClientData opt-in)"
))
}
fn run_for_format(
specification: &ProcessSpec,
format: OutputFormat,
) -> Result<(), crate::process::ProcessError> {
match format {
OutputFormat::Human => run(specification),
OutputFormat::Json => run_quiet(specification),
}
}