mecha10-cli 0.1.47

Mecha10 CLI tool
Documentation
//! Topology command handler
//!
//! Orchestrates topology analysis using TopologyService to display project structure,
//! nodes, pub/sub topics, and service ports.

use anyhow::Result;
use console::style;
use crossterm::{
    event::{self, Event},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::{backend::CrosstermBackend, Terminal};
use std::io::stdout;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;

use crate::commands::topology::{OutputFormat, TopologyArgs};
use crate::context::CliContext;
use crate::ui::TopologyTui;

/// Handle topology command
///
/// Performs static analysis of the project to visualize:
/// - Redis connection and port
/// - Service ports (HTTP API, database, etc.)
/// - Nodes and their pub/sub topic relationships
/// - Topic-centric view of publishers/subscribers
///
/// # Arguments
///
/// * `ctx` - CLI execution context
/// * `args` - Topology command arguments
pub async fn handle_topology(ctx: &mut CliContext, args: &TopologyArgs) -> Result<()> {
    // Analyze project topology using service
    let topology = ctx.topology().analyze().await?;

    // Format and display output
    match args.format {
        OutputFormat::Json => {
            let json = serde_json::to_string_pretty(&topology)?;
            println!("{}", json);
        }
        OutputFormat::Table => {
            // Use TUI for interactive table view
            display_tui_format(topology).await?;
        }
        OutputFormat::Graph => {
            display_graph_format(&topology, args)?;
        }
    }

    Ok(())
}

/// Display topology in interactive TUI format
async fn display_tui_format(topology: crate::services::Topology) -> Result<()> {
    // Setup terminal for TUI
    enable_raw_mode()?;
    let mut stdout_handle = stdout();
    execute!(stdout_handle, EnterAlternateScreen)?;
    let backend = CrosstermBackend::new(stdout_handle);
    let mut terminal = Terminal::new(backend)?;

    // Create topology TUI
    let mut tui = TopologyTui::new(topology);

    // Setup signal handler
    let running = Arc::new(AtomicBool::new(true));
    let r = running.clone();

    ctrlc::set_handler(move || {
        r.store(false, Ordering::SeqCst);
    })?;

    // Main event loop
    let result = loop {
        // Check interrupt flag
        if !running.load(Ordering::SeqCst) || tui.should_quit() {
            break Ok(());
        }

        // Draw TUI
        if let Err(e) = terminal.draw(|f| tui.draw(f)) {
            break Err(e.into());
        }

        // Poll for events
        if event::poll(Duration::from_millis(100))? {
            if let Event::Key(key_event) = event::read()? {
                tui.handle_key(key_event);
            }
        }
    };

    // Cleanup terminal
    disable_raw_mode()?;
    execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
    terminal.show_cursor()?;

    result
}

/// Display topology in graph format (ASCII art)
fn display_graph_format(topology: &crate::services::Topology, args: &TopologyArgs) -> Result<()> {
    println!();
    println!("{}", style(format!("Project: {}", topology.project_name)).bold());
    println!();

    // Show infrastructure
    if !args.nodes_only && !args.topics_only {
        println!("{}", style("Infrastructure:").bold());
        println!("  └─ Redis: {}:{}", topology.redis.host, topology.redis.port);
        for service in &topology.services {
            println!("  └─ {}: {}:{}", service.name, service.host, service.port);
        }
        println!();
    }

    // Show nodes and their connections
    if !args.ports_only {
        println!("{}", style("Node Topology:").bold());
        println!();

        for node in &topology.nodes {
            println!("{}", style(&node.name).cyan().bold());

            // Publishers
            for topic in &node.publishes {
                println!("");
                println!("  ├──[pub]──> {}", style(&topic.path).green());
            }

            // Subscribers
            for topic in &node.subscribes {
                println!("");
                println!("  └──[sub]──> {}", style(&topic.path).blue());
            }

            println!();
        }
    }

    Ok(())
}