mecha10-cli 0.1.47

Mecha10 CLI tool
Documentation
//! Status command handler
//!
//! Orchestrates services to display project and infrastructure status.

use crate::context::CliContext;
use anyhow::Result;

/// Handle the status command
///
/// Displays:
/// - Project information (name, platform, version)
/// - Node count
/// - Infrastructure service health (Redis, Postgres)
///
/// # Arguments
///
/// * `ctx` - CLI execution context
pub async fn handle_status(ctx: &mut CliContext) -> Result<()> {
    println!();
    println!("📊 Mecha10 Project Status");
    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
    println!();

    // Check if we're in a project
    if !ctx.is_project_initialized() {
        println!("⚠️  Not in a Mecha10 project directory");
        println!();
        println!("Run `mecha10 init` to create a new project");
        return Ok(());
    }

    // Load project configuration
    let config = ctx.load_project_config().await?;

    // Display project info
    println!("Project Information:");
    println!("  Robot ID: {}", config.robot.id);
    println!();

    // Get node count using ProjectService
    let project = ctx.project()?;
    match project.list_nodes() {
        Ok(nodes) => {
            println!("Nodes: {} configured", nodes.len());
            for node in &nodes {
                println!("{}", node);
            }
            println!();
        }
        Err(_) => {
            println!("Nodes: 0 configured");
            println!();
        }
    }

    // Check infrastructure services
    println!("Infrastructure Services:");
    println!();

    // Redis status
    let redis_url = ctx.redis_url().to_string();

    print!("  Redis: ");
    match ctx.redis() {
        Ok(redis_service) => {
            if redis_service.is_healthy().await {
                println!("✅ Connected");
                println!("    URL: {}", redis_url);
            } else {
                println!("❌ Not reachable");
                println!("    URL: {}", redis_url);
                println!("    Hint: Start Redis with `docker compose up -d` or `redis-server`");
            }
        }
        Err(_) => {
            println!("❌ Invalid URL");
            println!("    URL: {}", redis_url);
        }
    }
    println!();

    // Postgres status (if configured)
    if let Some(database_url) = ctx.postgres_url().map(|s| s.to_string()) {
        print!("  PostgreSQL: ");
        // Simple check - try to connect
        match check_postgres_health(&database_url).await {
            true => {
                println!("✅ Connected");
                println!("    URL: {}", mask_database_url(&database_url));
            }
            false => {
                println!("❌ Not reachable");
                println!("    URL: {}", mask_database_url(&database_url));
            }
        }
        println!();
    }

    // Docker status
    println!("  Docker:");
    let docker = ctx.docker();
    match docker.check_installation() {
        Ok(info) => {
            println!("{}", info.version);
            println!("    Compose: {}", info.compose_version);
        }
        Err(_) => {
            println!("    ❌ Not installed");
        }
    }
    println!();

    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
    println!();

    Ok(())
}

/// Check PostgreSQL health
async fn check_postgres_health(url: &str) -> bool {
    // Simple TCP connection check to avoid pulling in postgres dependencies
    // In a real implementation, you might use sqlx or tokio-postgres
    if let Some(host_port) = url.split('@').nth(1) {
        if let Some(host) = host_port.split('/').next() {
            if let Ok(stream) = tokio::net::TcpStream::connect(host).await {
                drop(stream);
                return true;
            }
        }
    }
    false
}

/// Mask sensitive parts of database URL
fn mask_database_url(url: &str) -> String {
    if let Some(at_pos) = url.find('@') {
        if let Some(proto_end) = url.find("://") {
            let protocol = &url[..proto_end + 3];
            let after_at = &url[at_pos..];
            return format!("{}***{}", protocol, after_at);
        }
    }
    url.to_string()
}