use crate::core::{apply_filters, parse_targets, resolve_target, Process};
use crate::error::Result;
use crate::ui::{colorize_status, format_duration, format_memory, Printer};
use clap::Args;
use colored::*;
use serde::Serialize;
#[derive(Args, Debug)]
pub struct InfoCommand {
#[arg(required = true)]
targets: Vec<String>,
#[arg(long, short = 'j')]
json: bool,
#[arg(long, short = 'v')]
verbose: bool,
#[arg(long = "in", short = 'i', num_args = 0..=1, default_missing_value = ".")]
pub in_dir: Option<String>,
#[arg(long = "by", short = 'b')]
pub by_name: Option<String>,
}
impl InfoCommand {
pub fn execute(&self) -> Result<()> {
let printer = Printer::from_flags(self.json, self.verbose);
let all_targets: Vec<String> = self.targets.iter().flat_map(|t| parse_targets(t)).collect();
let mut found = Vec::new();
let mut not_found = Vec::new();
let mut seen_pids = std::collections::HashSet::new();
for target in &all_targets {
match resolve_target(target) {
Ok(processes) => {
if processes.is_empty() {
not_found.push(target.clone());
} else {
for proc in processes {
if seen_pids.insert(proc.pid) {
found.push(proc);
}
}
}
}
Err(_) => not_found.push(target.clone()),
}
}
apply_filters(&mut found, &self.in_dir, &self.by_name);
if self.json {
printer.print_json(&InfoOutput {
action: "info",
success: !found.is_empty(),
found_count: found.len(),
not_found_count: not_found.len(),
processes: &found,
not_found: ¬_found,
});
} else {
for proc in &found {
self.print_process_info(proc);
}
if !not_found.is_empty() {
printer.warning(&format!("Not found: {}", not_found.join(", ")));
}
}
Ok(())
}
fn print_process_info(&self, proc: &Process) {
println!(
"{} Process {}",
"✓".green().bold(),
proc.pid.to_string().cyan().bold()
);
println!();
println!(" {} {}", "Name:".bright_black(), proc.name.white().bold());
println!(
" {} {}",
"PID:".bright_black(),
proc.pid.to_string().cyan()
);
if let Some(ref cwd) = proc.cwd {
println!(" {} {}", "Directory:".bright_black(), cwd);
}
if let Some(ref path) = proc.exe_path {
println!(" {} {}", "Path:".bright_black(), path);
}
if let Some(ref user) = proc.user {
println!(" {} {}", "User:".bright_black(), user);
}
if let Some(ppid) = proc.parent_pid {
println!(
" {} {}",
"Parent PID:".bright_black(),
ppid.to_string().cyan()
);
}
let status_str = format!("{:?}", proc.status);
let status_colored = colorize_status(&proc.status, &status_str);
println!(" {} {}", "Status:".bright_black(), status_colored);
println!(" {} {:.1}%", "CPU:".bright_black(), proc.cpu_percent);
println!(
" {} {}",
"Memory:".bright_black(),
format_memory(proc.memory_mb)
);
if let Some(start_time) = proc.start_time {
let duration = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs().saturating_sub(start_time))
.unwrap_or(0);
let uptime = format_duration(duration);
println!(" {} {}", "Uptime:".bright_black(), uptime);
}
if self.verbose {
if let Some(ref cmd) = proc.command {
println!(" {} {}", "Command:".bright_black(), cmd.bright_black());
}
}
println!();
}
}
#[derive(Serialize)]
struct InfoOutput<'a> {
action: &'static str,
success: bool,
found_count: usize,
not_found_count: usize,
processes: &'a [Process],
not_found: &'a [String],
}