use crate::config::ProjectConfig;
use crate::detectors::DetectorTrait;
use crate::models::{DetectionSource, Service, ServiceType};
use anyhow::Result;
use async_trait::async_trait;
use bollard::Docker;
use bollard::container::ListContainersOptions;
use std::path::Path;
use tracing::{debug, warn};
pub struct DockerDetector;
impl DockerDetector {
pub fn new() -> Self {
Self
}
}
#[async_trait]
impl DetectorTrait for DockerDetector {
fn name(&self) -> &str {
"DockerDetector"
}
fn priority(&self) -> i32 {
40
}
async fn detect(&self, project_path: &Path, _config: Option<&ProjectConfig>) -> Result<Vec<Service>> {
let mut services = Vec::new();
let docker = match Docker::connect_with_local_defaults() {
Ok(d) => d,
Err(e) => {
warn!("Could not connect to Docker: {}. Skipping Docker detection.", e);
return Ok(services);
}
};
let containers = match docker.list_containers(Some(ListContainersOptions::<String> {
all: false,
..Default::default()
})).await {
Ok(c) => c,
Err(e) => {
warn!("Could not list Docker containers: {}", e);
return Ok(services);
}
};
for container in containers {
let name = container.names
.and_then(|names| names.first().map(|n| n.trim_start_matches('/').to_string()))
.unwrap_or_else(|| "Unknown Container".to_string());
let id = container.id.unwrap_or_default();
if let Some(ports) = container.ports {
for port in ports {
if let Some(public_port) = port.public_port {
let private_port = port.private_port;
let mut service = Service::new(
format!("{} (Docker)", name),
ServiceType::DockerContainer,
"localhost".to_string(),
public_port,
DetectionSource::DockerCompose,
project_path.to_string_lossy().to_string(),
);
service.additional_ports.push(private_port);
service.tags.push("docker".to_string());
service.command_line = Some(format!("Container ID: {}", id));
services.push(service);
debug!("Detected Docker container {} on port {}", name, public_port);
}
}
}
}
Ok(services)
}
}