use std::time::Duration;
use crate::{
ContainerAsync, Image,
core::{client::Client, error::Result},
};
#[derive(Debug, Clone)]
pub struct HealthWaitStrategy {
poll_interval: Duration,
}
impl HealthWaitStrategy {
pub fn new() -> Self {
Self {
poll_interval: Duration::from_millis(100),
}
}
pub fn with_poll_interval(mut self, poll_interval: Duration) -> Self {
self.poll_interval = poll_interval;
self
}
}
impl Default for HealthWaitStrategy {
fn default() -> Self {
Self::new()
}
}
impl HealthWaitStrategy {
pub(crate) async fn wait_until_ready<I: Image>(
self,
client: &Client,
container: &ContainerAsync<I>,
) -> Result<()> {
#[cfg(target_os = "macos")]
{
let _ = (client, self.poll_interval);
Err(
crate::core::error::WaitContainerError::HealthCheckNotConfigured(
container.id().to_string(),
)
.into(),
)
}
#[cfg(target_os = "linux")]
{
use crate::core::client::HealthStatus;
use crate::core::error::WaitContainerError;
#[expect(clippy::infallible_destructuring_match)]
let docker = match client {
Client::Linux(c) => c,
#[cfg(target_os = "macos")]
Client::MacOs(_) => unreachable!("Linux block is not compiled on macOS"),
};
let id = container.id().to_string();
let mut seen_running = false;
loop {
let probe = docker.container_health(&id).await?;
if probe.running {
seen_running = true;
}
match probe.health {
Some(HealthStatus::Healthy) => return Ok(()),
Some(HealthStatus::Unhealthy) => {
return Err(WaitContainerError::Unhealthy(id).into());
}
Some(HealthStatus::Starting) => {
}
None => {
if seen_running {
return Err(WaitContainerError::HealthCheckNotConfigured(id).into());
}
}
}
tokio::time::sleep(self.poll_interval).await;
}
}
}
}