use console::style;
use std::path::Path;
type FileCheck = (&'static str, &'static str, fn(&Path) -> bool);
pub fn run() {
let mut ok = 0u32;
let mut warn = 0u32;
let mut err = 0u32;
let root = Path::new(".");
println!(
"{}",
style("rok check — project health").bold().underlined()
);
println!();
let checks: &[FileCheck] = &[
("Cargo.toml", "workspace manifest", |p| {
p.join("Cargo.toml").exists()
}),
(".env", "environment config", |p| p.join(".env").exists()),
(".env.example", "env template", |p| {
p.join(".env.example").exists()
}),
("src/main.rs", "application entry", |p| {
p.join("src/main.rs").exists()
}),
("src/lib.rs", "library entry", |p| {
p.join("src/lib.rs").exists()
}),
("src/state.rs", "app state", |p| {
p.join("src/state.rs").exists()
}),
("src/routes/", "routes directory", |p| {
p.join("src/routes").is_dir()
}),
("src/app/controllers/", "controllers directory", |p| {
p.join("src/app/controllers").is_dir()
}),
("src/app/models/", "models directory", |p| {
p.join("src/app/models").is_dir()
}),
("src/app/validators/", "validators directory", |p| {
p.join("src/app/validators").is_dir()
}),
(
"database/migrations/",
"migrations directory (rok layout)",
|p| p.join("database/migrations").is_dir(),
),
("migrations/", "migrations directory (flat layout)", |p| {
p.join("migrations").is_dir()
}),
(".github/workflows/ci.yml", "CI workflow", |p| {
p.join(".github/workflows/ci.yml").exists()
}),
("Dockerfile", "Docker build", |p| {
p.join("Dockerfile").exists()
}),
("docker-compose.yml", "Docker Compose", |p| {
p.join("docker-compose.yml").exists()
}),
(".dockerignore", "Docker ignore", |p| {
p.join(".dockerignore").exists()
}),
("roadmap.md", "roadmap", |p| p.join("roadmap.md").exists()),
("CHANGELOG.md", "changelog", |p| {
p.join("CHANGELOG.md").exists()
}),
("README.md", "readme", |p| p.join("README.md").exists()),
];
for (name, label, check) in checks {
if check(root) {
println!(
" {} {} ({})",
style("✔").green(),
name,
style(label).dim()
);
ok += 1;
} else {
println!(
" {} {} — {} ({})",
style("✖").red(),
name,
style("missing").red(),
style(label).dim()
);
err += 1;
}
}
let cargo_toml = root.join("Cargo.toml");
if cargo_toml.exists() {
if let Ok(content) = std::fs::read_to_string(&cargo_toml) {
if content.contains("[workspace]") {
println!(" {} workspace root detected", style("✔").green());
ok += 1;
} else {
println!(
" {} not a workspace root ([workspace] missing)",
style("⚠").yellow()
);
warn += 1;
}
if content.contains("[package]") {
println!(" {} binary crate defined", style("✔").green());
ok += 1;
}
}
}
let cli_check = std::process::Command::new("rok").arg("--version").output();
match cli_check {
Ok(output) if output.status.success() => {
let ver = String::from_utf8_lossy(&output.stdout).trim().to_string();
println!(
" {} rok CLI installed ({})",
style("✔").green(),
style(ver).dim()
);
ok += 1;
}
_ => {
println!(
" {} rok CLI not found — run: cargo install rok-cli",
style("⚠").yellow()
);
warn += 1;
}
}
let watch_check = std::process::Command::new("cargo")
.args(["watch", "--version"])
.output();
match watch_check {
Ok(output) if output.status.success() => {
println!(
" {} cargo-watch installed (hot reload available)",
style("✔").green()
);
ok += 1;
}
_ => {
println!(
" {} cargo-watch not found — install: cargo install cargo-watch",
style("⚠").yellow()
);
warn += 1;
}
}
println!();
println!(
" {} {} {}, {} {}, {} {}",
if err == 0 {
style("Summary:").green().bold()
} else {
style("Summary:").red().bold()
},
ok,
style("ok").green(),
warn,
style("warnings").yellow(),
err,
style("errors").red(),
);
}