darpan 0.1.0

Linux developer service monitoring utility with auto-detection, real-time health checks, and interactive TUI for databases, APIs, Docker containers, and more
Documentation
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();
        
        // Try to connect to Docker
        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);
            }
        };
        
        // List running containers
        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();
            
            // Extract port mappings
            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)
    }
}