use crate::port::PortInfo;
use anyhow::Result;
use colored::*;
use inquire::{Confirm, MultiSelect};
pub fn display_port_not_found(port: u16) {
println!("{}", format!("端口 {port} 未被占用").yellow());
}
pub fn select_processes_to_kill(port_infos: Vec<PortInfo>) -> Result<Vec<PortInfo>> {
if port_infos.is_empty() {
println!("{}", "未找到任何占用指定端口的进程".yellow());
return Ok(vec![]);
}
let options: Vec<String> = port_infos
.iter()
.map(|info| {
format!(
"端口 {} - {} (PID: {}) - {}",
info.port,
info.process.name,
info.process.pid,
info.process.cmd.join(" ")
)
})
.collect();
let defaults: Vec<usize> = (0..options.len()).collect();
let selected = MultiSelect::new("选择要终止的进程:", options)
.with_default(&defaults)
.prompt()?;
let mut result = Vec::new();
for selection in selected {
for info in &port_infos {
let expected = format!(
"端口 {} - {} (PID: {}) - {}",
info.port,
info.process.name,
info.process.pid,
info.process.cmd.join(" ")
);
if selection == expected {
result.push(info.clone());
break;
}
}
}
if result.is_empty() {
println!("{}", "未选择任何进程".yellow());
return Ok(vec![]);
}
let confirm = Confirm::new("确认终止这些进程?")
.with_default(false)
.prompt()?;
if confirm {
Ok(result)
} else {
println!("{}", "操作已取消".yellow());
Ok(vec![])
}
}
pub fn display_kill_results(results: &[(u32, Result<()>)]) {
for (pid, result) in results {
match result {
Ok(()) => println!("{} {}", "✓".green(), format!("成功终止进程 {pid}").green()),
Err(e) => println!(
"{} {}: {}",
"✗".red(),
format!("无法终止进程 {pid}").red(),
e
),
}
}
}
fn truncate_string(s: &str, max_len: usize) -> String {
if s.len() <= max_len {
s.to_string()
} else {
format!("{}...", &s[..max_len.saturating_sub(3)])
}
}
pub fn display_error(error: &anyhow::Error) {
eprintln!("{} {}", "错误:".red().bold(), error);
}
pub fn display_ports_tree(ports: &[u16], port_infos: Vec<PortInfo>) {
if ports.is_empty() {
return;
}
println!("{}", "⚡ 端口查询结果".cyan().bold());
println!();
let mut port_map = std::collections::HashMap::new();
for info in port_infos {
port_map.insert(info.port, info);
}
let total = ports.len();
for (index, &port) in ports.iter().enumerate() {
let is_last = index == total - 1;
let branch = if is_last { "└─" } else { "├─" };
let continuation = if is_last { " " } else { "│ " };
if let Some(info) = port_map.get(&port) {
println!(
"{} {} {}",
branch,
format!("{port}").yellow().bold(),
"✓".green()
);
println!(
"{}├─ {}: {} ({})",
continuation,
"进程".cyan(),
info.process.name.green(),
format!("{}", info.process.pid).bright_black()
);
let cmd = truncate_string(&info.process.cmd.join(" "), 60);
println!(
"{}├─ {}: {}",
continuation,
"命令".cyan(),
cmd.bright_black()
);
println!(
"{}└─ {}: {} CPU, {} 内存",
continuation,
"资源".cyan(),
format!("{:.1}%", info.process.cpu_usage).magenta(),
format!("{} MB", info.process.memory / 1024 / 1024).magenta()
);
} else {
println!(
"{} {} {} {}",
branch,
format!("{port}").yellow().bold(),
"✗".red(),
"(空闲)".bright_black()
);
}
if !is_last {
println!("{continuation}");
}
}
}
pub fn display_ports_tree_all(port_infos: Vec<PortInfo>) {
if port_infos.is_empty() {
println!("{}", "当前没有端口被占用".yellow());
return;
}
println!(
"{} {}",
"⚡ 端口占用情况".cyan().bold(),
format!("(共 {} 个)", port_infos.len()).bright_black()
);
println!();
let total = port_infos.len();
for (index, info) in port_infos.iter().enumerate() {
let is_last = index == total - 1;
let branch = if is_last { "└─" } else { "├─" };
let continuation = if is_last { " " } else { "│ " };
println!(
"{} {} {}",
branch,
format!("{}", info.port).yellow().bold(),
"✓".green()
);
println!(
"{}├─ {}: {} ({})",
continuation,
"进程".cyan(),
info.process.name.green(),
format!("{}", info.process.pid).bright_black()
);
let cmd = truncate_string(&info.process.cmd.join(" "), 60);
println!(
"{}├─ {}: {}",
continuation,
"命令".cyan(),
cmd.bright_black()
);
println!(
"{}└─ {}: {} CPU, {} 内存",
continuation,
"资源".cyan(),
format!("{:.1}%", info.process.cpu_usage).magenta(),
format!("{} MB", info.process.memory / 1024 / 1024).magenta()
);
if !is_last {
println!("{continuation}");
}
}
}