use crate::config::{project_root, Environment};
use crate::ui;
use anyhow::{Context, Result};
use std::process::{Command, ExitStatus, Stdio};
pub fn compose(env: Environment, args: &[&str]) -> Result<ExitStatus> {
let root = project_root()?;
let compose_file = root.join(env.compose_file());
let mut cmd = Command::new("docker");
cmd.current_dir(&root)
.arg("compose")
.arg("-f")
.arg(&compose_file)
.args(args)
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit());
ui::show_command(&format!(
"docker compose -f {} {}",
env.compose_file(),
args.join(" ")
));
let status = cmd
.status()
.with_context(|| "Failed to execute docker compose command")?;
Ok(status)
}
pub fn compose_output(env: Environment, args: &[&str]) -> Result<String> {
let root = project_root()?;
let compose_file = root.join(env.compose_file());
let output = Command::new("docker")
.current_dir(&root)
.arg("compose")
.arg("-f")
.arg(&compose_file)
.args(args)
.output()
.with_context(|| "Failed to execute docker compose command")?;
if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).to_string())
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("Docker compose failed: {stderr}")
}
}
pub fn exec(env: Environment, service: &str, cmd: &[&str]) -> Result<ExitStatus> {
let mut args = vec!["exec", service];
args.extend(cmd);
compose(env, &args)
}
pub fn exec_no_tty(env: Environment, service: &str, cmd: &[&str]) -> Result<ExitStatus> {
let mut args = vec!["exec", "-T", service];
args.extend(cmd);
compose(env, &args)
}
pub fn check_docker() -> Result<()> {
let status = Command::new("docker")
.arg("--version")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.with_context(|| "Docker is not installed or not in PATH")?;
if !status.success() {
anyhow::bail!("Docker is not running");
}
Ok(())
}
pub fn is_service_running(env: Environment, service: &str) -> Result<bool> {
let output = compose_output(env, &["ps", "--format", "{{.State}}", service])?;
Ok(output.trim() == "running")
}
pub fn running_services(env: Environment) -> Result<Vec<String>> {
let output = compose_output(env, &["ps", "--format", "{{.Service}}"])?;
Ok(output.lines().map(String::from).collect())
}