#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DockerStatus {
Available,
NotInstalled,
NotRunning,
Disabled,
}
impl DockerStatus {
pub fn is_ok(&self) -> bool {
matches!(self, DockerStatus::Available)
}
pub fn as_str(&self) -> &'static str {
match self {
DockerStatus::Available => "available",
DockerStatus::NotInstalled => "not installed",
DockerStatus::NotRunning => "not running",
DockerStatus::Disabled => "disabled",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Platform {
MacOS,
Linux,
Windows,
}
impl Platform {
pub fn current() -> Self {
match std::env::consts::OS {
"macos" => Platform::MacOS,
"windows" => Platform::Windows,
_ => Platform::Linux,
}
}
pub fn install_hint(&self) -> &'static str {
match self {
Platform::MacOS => {
"Install Docker Desktop: https://docs.docker.com/desktop/install/mac-install/"
}
Platform::Linux => "Install Docker Engine: https://docs.docker.com/engine/install/",
Platform::Windows => {
"Install Docker Desktop: https://docs.docker.com/desktop/install/windows-install/"
}
}
}
pub fn start_hint(&self) -> &'static str {
match self {
Platform::MacOS => {
"Start Docker Desktop from Applications, or run: open -a Docker\n\n To auto-start at login: System Settings > General > Login Items > add Docker.app"
}
Platform::Linux => "Start the Docker daemon: sudo systemctl start docker",
Platform::Windows => "Start Docker Desktop from the Start menu",
}
}
}
pub struct DockerDetection {
pub status: DockerStatus,
pub platform: Platform,
}
pub async fn check_docker() -> DockerDetection {
let platform = Platform::current();
if !docker_binary_exists() {
return DockerDetection {
status: DockerStatus::NotInstalled,
platform,
};
}
if crate::sandbox::connect_docker().await.is_ok() {
return DockerDetection {
status: DockerStatus::Available,
platform,
};
}
#[cfg(windows)]
if docker_cli_daemon_reachable() {
return DockerDetection {
status: DockerStatus::Available,
platform,
};
}
DockerDetection {
status: DockerStatus::NotRunning,
platform,
}
}
fn docker_binary_exists() -> bool {
#[cfg(unix)]
{
std::process::Command::new("which")
.arg("docker")
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.is_ok_and(|s| s.success())
}
#[cfg(windows)]
{
std::process::Command::new("where")
.arg("docker")
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.is_ok_and(|s| s.success())
}
}
#[cfg(windows)]
fn docker_cli_daemon_reachable() -> bool {
let stdout = std::process::Stdio::null();
let stderr = std::process::Stdio::null();
let version_ok = std::process::Command::new("docker")
.args(["version", "--format", "{{.Server.Version}}"])
.stdout(stdout)
.stderr(stderr)
.status()
.is_ok_and(|s| s.success());
if version_ok {
return true;
}
std::process::Command::new("docker")
.args(["info", "--format", "{{.ServerVersion}}"])
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.is_ok_and(|s| s.success())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_platform() {
let platform = Platform::current();
match platform {
Platform::MacOS | Platform::Linux | Platform::Windows => {}
}
}
#[test]
fn test_install_hint_not_empty() {
for platform in [Platform::MacOS, Platform::Linux, Platform::Windows] {
assert!(!platform.install_hint().is_empty());
assert!(!platform.start_hint().is_empty());
}
}
#[test]
fn test_docker_status_display() {
assert_eq!(DockerStatus::Available.as_str(), "available");
assert_eq!(DockerStatus::NotInstalled.as_str(), "not installed");
assert_eq!(DockerStatus::NotRunning.as_str(), "not running");
assert_eq!(DockerStatus::Disabled.as_str(), "disabled");
}
#[test]
fn test_docker_status_is_ok() {
assert!(DockerStatus::Available.is_ok());
assert!(!DockerStatus::NotInstalled.is_ok());
assert!(!DockerStatus::NotRunning.is_ok());
assert!(!DockerStatus::Disabled.is_ok());
}
#[tokio::test]
async fn test_check_docker_returns_valid_status() {
let result = check_docker().await;
match result.status {
DockerStatus::Available | DockerStatus::NotInstalled | DockerStatus::NotRunning => {}
DockerStatus::Disabled => panic!("check_docker should never return Disabled"),
}
}
}