use crate::ui;
use anyhow::Result;
pub struct DependencyCheck {
pub docker: bool,
pub docker_compose: bool,
pub docker_running: bool,
pub write_access: bool,
}
impl DependencyCheck {
pub fn check_all() -> Self {
Self {
docker: check_command("docker"),
docker_compose: check_command("docker-compose") || check_command("docker"),
docker_running: check_docker_running(),
write_access: check_garden_dir_writeable(),
}
}
pub fn is_ready(&self) -> bool {
self.docker && self.docker_compose && self.docker_running && self.write_access
}
pub fn print_report(&self) {
println!();
let items = [
("docker", self.docker, "🐳", "Docker binary found"),
(
"docker-compose",
self.docker_compose,
"📦",
"docker-compose / 'docker compose' available",
),
(
"docker daemon",
self.docker_running,
"⚙️ ",
"Docker daemon is running",
),
(
"~/.garden/",
self.write_access,
"📁",
"Can write to garden directory",
),
];
for (name, passed, icon, desc) in &items {
if *passed {
ui::success(&format!("{icon} {name:<18} {desc}"));
} else {
ui::error(&format!("{icon} {name:<18} missing"));
}
}
ui::divider();
if self.is_ready() {
println!();
ui::success("All dependencies satisfied — your garden bed is ready!");
} else {
println!();
ui::error("Some dependencies are missing.\n");
self.print_installation_guide();
}
}
fn print_installation_guide(&self) {
let _guides: Vec<(&str, &str)> = vec![];
if !self.docker {
ui::hint("🐳 Install Docker:");
println!(" macOS: brew install --cask docker");
println!(" Ubuntu: curl -fsSL https://get.docker.com | sh");
println!();
}
if !self.docker_compose {
ui::hint("📦 Install Docker Compose:");
println!(" Usually included with Docker Desktop");
println!();
}
if !self.docker_running {
ui::hint("⚙️ Start Docker daemon:");
println!(" macOS: Open Docker Desktop app");
println!(" Linux: sudo systemctl start docker");
println!();
}
if !self.write_access {
ui::hint("📁 Fix ~/.garden/ permissions:");
println!(" mkdir -p ~/.garden");
println!();
}
}
}
fn check_command(cmd: &str) -> bool {
std::process::Command::new(cmd)
.arg("--version")
.output()
.is_ok()
}
fn check_docker_running() -> bool {
std::process::Command::new("docker")
.args(["info"])
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
fn check_garden_dir_writeable() -> bool {
let garden_dir = dirs::home_dir()
.map(|h| h.join(".garden"))
.unwrap_or_else(|| std::path::PathBuf::from(".garden"));
if !garden_dir.exists() {
std::fs::create_dir_all(&garden_dir).is_ok()
} else {
std::fs::metadata(&garden_dir)
.map(|m| m.permissions().readonly() == false)
.unwrap_or(false)
}
}
pub fn check() -> Result<()> {
let check = DependencyCheck::check_all();
check.print_report();
if !check.is_ready() {
anyhow::bail!("Missing dependencies. Please install the missing components above.");
}
Ok(())
}