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;
pub async fn handle_topology(ctx: &mut CliContext, args: &TopologyArgs) -> Result<()> {
let topology = ctx.topology().analyze().await?;
match args.format {
OutputFormat::Json => {
let json = serde_json::to_string_pretty(&topology)?;
println!("{}", json);
}
OutputFormat::Table => {
display_tui_format(topology).await?;
}
OutputFormat::Graph => {
display_graph_format(&topology, args)?;
}
}
Ok(())
}
async fn display_tui_format(topology: crate::services::Topology) -> Result<()> {
enable_raw_mode()?;
let mut stdout_handle = stdout();
execute!(stdout_handle, EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout_handle);
let mut terminal = Terminal::new(backend)?;
let mut tui = TopologyTui::new(topology);
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
ctrlc::set_handler(move || {
r.store(false, Ordering::SeqCst);
})?;
let result = loop {
if !running.load(Ordering::SeqCst) || tui.should_quit() {
break Ok(());
}
if let Err(e) = terminal.draw(|f| tui.draw(f)) {
break Err(e.into());
}
if event::poll(Duration::from_millis(100))? {
if let Event::Key(key_event) = event::read()? {
tui.handle_key(key_event);
}
}
};
disable_raw_mode()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
terminal.show_cursor()?;
result
}
fn display_graph_format(topology: &crate::services::Topology, args: &TopologyArgs) -> Result<()> {
println!();
println!("{}", style(format!("Project: {}", topology.project_name)).bold());
println!();
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!();
}
if !args.ports_only {
println!("{}", style("Node Topology:").bold());
println!();
for node in &topology.nodes {
println!("{}", style(&node.name).cyan().bold());
for topic in &node.publishes {
println!(" │");
println!(" ├──[pub]──> {}", style(&topic.path).green());
}
for topic in &node.subscribes {
println!(" │");
println!(" └──[sub]──> {}", style(&topic.path).blue());
}
println!();
}
}
Ok(())
}