oph 0.1.0

OpenHealth CLI - Universal development tool for the OpenHealth team
Documentation
use crate::config::{project_root, Environment};
use crate::ui;
use anyhow::{Context, Result};
use std::process::{Command, ExitStatus, Stdio};

/// Execute a docker compose command
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)
}

/// Execute a docker compose command and capture output
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}")
    }
}

/// Execute a command inside a container
pub fn exec(env: Environment, service: &str, cmd: &[&str]) -> Result<ExitStatus> {
    let mut args = vec!["exec", service];
    args.extend(cmd);
    compose(env, &args)
}

/// Execute a command inside a container without TTY (for piping)
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)
}

/// Check if docker is available
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(())
}

/// Check if a service is running
pub fn is_service_running(env: Environment, service: &str) -> Result<bool> {
    let output = compose_output(env, &["ps", "--format", "{{.State}}", service])?;
    Ok(output.trim() == "running")
}

/// Get list of running services
pub fn running_services(env: Environment) -> Result<Vec<String>> {
    let output = compose_output(env, &["ps", "--format", "{{.Service}}"])?;
    Ok(output.lines().map(String::from).collect())
}