#![allow(clippy::uninlined_format_args)]
mod args;
mod cli_dispatch;
mod cli_formatter;
mod commands;
mod mcp_service;
mod output;
mod setup;
mod trace;
mod tui;
mod tui_export;
mod tui_formatter;
mod tui_settings;
use anyhow::Result;
use args::Cli;
use clap::{CommandFactory, Parser};
use netscli_core::{Ops, OpsConfig};
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
print_first_run_hint(&cli);
let db = commands::try_init_db().await;
let ops = Ops::new(OpsConfig {
concurrency: cli
.concurrency
.unwrap_or_else(|| tui_settings::load_settings().max_concurrent_probes),
..Default::default()
});
let local_addr = tokio::task::spawn_blocking(netscli_core::detect_default_ipv4_addr)
.await
.ok()
.flatten()
.map(|ip| ip.to_string());
if let Some(command) = &cli.command {
cli_dispatch::run_command(
command,
cli_dispatch::CommandContext {
ops: &ops,
db: db.as_ref(),
local_addr: local_addr.as_deref(),
},
)
.await?;
} else if terminal_available() {
tui::run_tui(cli.concurrency).await?;
} else {
Cli::command().print_help()?;
println!();
}
Ok(())
}
fn terminal_available() -> bool {
use std::io::IsTerminal;
std::io::stdin().is_terminal() && std::io::stdout().is_terminal()
}
fn print_first_run_hint(cli: &Cli) {
if cli.command.is_none() && terminal_available() && !setup::config_exists() {
#[cfg(feature = "pcap")]
eprintln!(
"netscli: first run detected. Type `/help` for commands, or run `netscli setup` to install optional dependencies (libpcap, tcpdump)."
);
#[cfg(not(feature = "pcap"))]
eprintln!(
"netscli: first run detected. Type `/help` for commands. (This build has pcap disabled — rebuild with --features pcap to enable capture.)"
);
}
}