#![allow(dead_code)]
use crate::paths;
use crate::services::docker::types::ContainerInfo;
use anyhow::{Context, Result};
use std::path::Path;
use std::process::Command;
use std::time::Duration;
pub async fn is_container_running(container_name: &str) -> Result<bool> {
let output = Command::new("docker")
.args([
"ps",
"--filter",
&format!("name={}", container_name),
"--format",
"{{.Names}}",
])
.output()
.context("Failed to check container status")?;
if !output.status.success() {
return Ok(false);
}
let stdout = String::from_utf8_lossy(&output.stdout);
Ok(stdout.contains(container_name))
}
pub async fn list_containers() -> Result<Vec<ContainerInfo>> {
let output = Command::new("docker")
.args(["ps", "--format", "{{.Names}}\t{{.Status}}\t{{.Ports}}"])
.output()
.context("Failed to list containers")?;
if !output.status.success() {
return Err(anyhow::anyhow!("Failed to list containers"));
}
let stdout = String::from_utf8_lossy(&output.stdout);
let mut containers = Vec::new();
for line in stdout.lines() {
let parts: Vec<&str> = line.split('\t').collect();
if parts.len() >= 2 {
containers.push(ContainerInfo {
name: parts[0].to_string(),
status: parts[1].to_string(),
ports: parts.get(2).map(|s| s.to_string()),
});
}
}
Ok(containers)
}
pub fn compose_file_exists(compose_file: Option<&str>, path: Option<&Path>) -> bool {
let compose_path = if let Some(p) = path {
p.join(paths::docker::COMPOSE_FILE)
} else if let Some(file) = compose_file {
std::path::PathBuf::from(file)
} else {
std::path::PathBuf::from(paths::docker::COMPOSE_FILE)
};
compose_path.exists()
}
pub async fn wait_for_healthy(container_name: &str, timeout: Duration) -> Result<()> {
let start = std::time::Instant::now();
while start.elapsed() < timeout {
if is_container_running(container_name).await? {
return Ok(());
}
tokio::time::sleep(Duration::from_millis(500)).await;
}
Err(anyhow::anyhow!(
"Timeout waiting for container {} to be healthy",
container_name
))
}