shiguredo_container 2026.1.0-canary.8

Runtime-agnostic container library for Rust on macOS and Linux
Documentation
//! 終了待機戦略。元の 0.27 の `core::wait::exit_strategy` と同一シグネチャ。

use std::time::Duration;

use crate::{
    ContainerAsync, Image,
    core::{client::Client, error::Result},
};

/// コンテナの終了を待つ戦略。
///
/// バックグラウンドの `containerWait` が観測した exit code と
/// `container_state` のポーリングで終了を判定する。
#[derive(Debug, Clone)]
pub struct ExitWaitStrategy {
    expected_code: Option<i64>,
    poll_interval: Duration,
}

impl ExitWaitStrategy {
    /// デフォルト設定 (exit code 不問、100ms ポーリング) で戦略を作る。
    pub fn new() -> Self {
        Self {
            expected_code: None,
            poll_interval: Duration::from_millis(100),
        }
    }

    /// ポーリング間隔を設定する。
    pub fn with_poll_interval(mut self, poll_interval: Duration) -> Self {
        self.poll_interval = poll_interval;
        self
    }

    /// 期待する exit code を設定する。不一致の場合はエラーになる。
    pub fn with_exit_code(mut self, expected_code: i64) -> Self {
        self.expected_code = Some(expected_code);
        self
    }
}

impl Default for ExitWaitStrategy {
    fn default() -> Self {
        Self::new()
    }
}

impl ExitWaitStrategy {
    pub(crate) async fn wait_until_ready<I: Image>(
        self,
        client: &Client,
        container: &ContainerAsync<I>,
    ) -> Result<()> {
        loop {
            // containerList (container_state) は exit code を返さないため、
            // バックグラウンドの containerWait が観測した値で判定する。
            if let Some(actual) = container.exit_code_hint() {
                if let Some(expected_code) = self.expected_code
                    && actual != expected_code
                {
                    return Err(crate::core::error::WaitContainerError::UnexpectedExitCode {
                        expected: expected_code,
                        actual: Some(actual),
                    }
                    .into());
                }
                return Ok(());
            }

            let state = match client {
                #[cfg(target_os = "macos")]
                Client::MacOs(c) => c.container_state(container.id()).await?,
                #[cfg(target_os = "linux")]
                Client::Linux(c) => c.container_state(container.id()).await?,
            };

            // exit code の検証が不要なら停止の観測だけで完了。検証が必要な場合は
            // containerWait が exit code を書き込むまで待つ (startup_timeout で打ち切られる)。
            if !state.running && self.expected_code.is_none() {
                return Ok(());
            }

            tokio::time::sleep(self.poll_interval).await;
        }
    }
}