pub mod cli;
pub mod commands;
pub mod docker;
pub mod history;
pub mod interactive;
pub mod output;
pub mod platform;
pub mod top;
pub mod types;
pub mod watch;
pub use cli::Cli;
use std::io;
use std::time::Duration;
use anyhow::Result;
use clap::CommandFactory;
use clap_complete::generate;
use types::PortInfo;
pub fn run(cli: Cli) -> Result<()> {
if cli.interactive {
return run_interactive(&cli);
}
if cli.watch {
let filter = match &cli.command {
Some(cli::Commands::List) => None,
Some(cli::Commands::Kill { .. }) => {
anyhow::bail!("Cannot use --watch with kill command");
}
Some(cli::Commands::Completions { .. }) => {
anyhow::bail!("Cannot use --watch with completions command");
}
Some(cli::Commands::Top { .. }) => {
anyhow::bail!("Cannot use --watch with top command (top has its own refresh)");
}
Some(cli::Commands::History { .. }) => {
anyhow::bail!("Cannot use --watch with history command");
}
None => cli.query.clone(),
};
return watch::run(watch::WatchOptions {
interval: Duration::from_secs_f64(cli.interval),
json: cli.json,
filter,
connections: cli.connections,
sort: cli.sort,
protocol: cli.protocol,
});
}
match &cli.command {
Some(cli::Commands::List) => {
commands::list::execute(cli.json, cli.connections, cli.sort, cli.protocol)
}
Some(cli::Commands::Kill { target, force, all }) => {
commands::kill::execute(target, *force, *all)
}
Some(cli::Commands::Top { connections }) => {
top::run(*connections)
}
Some(cli::Commands::Completions { shell }) => {
generate(*shell, &mut Cli::command(), "ports", &mut io::stdout());
Ok(())
}
Some(cli::Commands::History { action }) => {
match action {
cli::HistoryAction::Record { connections } => {
commands::history::record(*connections, cli.json)
}
cli::HistoryAction::Show { port, process, hours, limit } => {
commands::history::show(*port, process.clone(), Some(*hours), *limit, cli.json)
}
cli::HistoryAction::Timeline { port, hours } => {
commands::history::timeline(*port, *hours, cli.json)
}
cli::HistoryAction::Stats => {
commands::history::stats(cli.json)
}
cli::HistoryAction::Clean { keep } => {
commands::history::cleanup(*keep, cli.json)
}
}
}
None => match &cli.query {
Some(query) => {
commands::query::execute(query, cli.json, cli.connections, cli.sort, cli.protocol)
}
None => commands::list::execute(cli.json, cli.connections, cli.sort, cli.protocol),
},
}
}
fn run_interactive(cli: &Cli) -> Result<()> {
let ports = if cli.connections {
platform::get_connections()?
} else {
platform::get_listening_ports()?
};
let ports = PortInfo::filter_protocol(ports, cli.protocol);
let mut ports = PortInfo::enrich_with_docker(ports);
if let Some(query) = &cli.query {
let query_lower = query.to_lowercase();
ports = if let Ok(port_num) = query.parse::<u16>() {
ports.into_iter().filter(|p| p.port == port_num).collect()
} else {
ports
.into_iter()
.filter(|p| {
p.process_name.to_lowercase().contains(&query_lower)
|| p.container
.as_ref()
.map(|c| c.to_lowercase().contains(&query_lower))
.unwrap_or(false)
})
.collect()
};
}
PortInfo::sort_vec(&mut ports, cli.sort);
interactive::select_and_kill(&ports)
}