proc_cli/commands/
info.rs1use crate::core::{apply_filters, parse_targets, resolve_target, Process};
11use crate::error::Result;
12use crate::ui::{colorize_status, format_duration, format_memory, Printer};
13use clap::Args;
14use colored::*;
15use serde::Serialize;
16
17#[derive(Args, Debug)]
19pub struct InfoCommand {
20 #[arg(required = true)]
22 targets: Vec<String>,
23
24 #[arg(long, short = 'j')]
26 json: bool,
27
28 #[arg(long, short = 'v')]
30 verbose: bool,
31
32 #[arg(long = "in", short = 'i', num_args = 0..=1, default_missing_value = ".")]
34 pub in_dir: Option<String>,
35
36 #[arg(long = "by", short = 'b')]
38 pub by_name: Option<String>,
39}
40
41impl InfoCommand {
42 pub fn execute(&self) -> Result<()> {
44 let printer = Printer::from_flags(self.json, self.verbose);
45
46 let all_targets: Vec<String> = self.targets.iter().flat_map(|t| parse_targets(t)).collect();
48
49 let mut found = Vec::new();
50 let mut not_found = Vec::new();
51 let mut seen_pids = std::collections::HashSet::new();
52
53 for target in &all_targets {
54 match resolve_target(target) {
55 Ok(processes) => {
56 if processes.is_empty() {
57 not_found.push(target.clone());
58 } else {
59 for proc in processes {
60 if seen_pids.insert(proc.pid) {
62 found.push(proc);
63 }
64 }
65 }
66 }
67 Err(_) => not_found.push(target.clone()),
68 }
69 }
70
71 apply_filters(&mut found, &self.in_dir, &self.by_name);
73
74 if self.json {
75 printer.print_json(&InfoOutput {
76 action: "info",
77 success: !found.is_empty(),
78 found_count: found.len(),
79 not_found_count: not_found.len(),
80 processes: &found,
81 not_found: ¬_found,
82 });
83 } else {
84 for proc in &found {
85 self.print_process_info(proc);
86 }
87
88 if !not_found.is_empty() {
89 printer.warning(&format!("Not found: {}", not_found.join(", ")));
90 }
91 }
92
93 Ok(())
94 }
95
96 fn print_process_info(&self, proc: &Process) {
97 println!(
98 "{} Process {}",
99 "✓".green().bold(),
100 proc.pid.to_string().cyan().bold()
101 );
102 println!();
103 println!(" {} {}", "Name:".bright_black(), proc.name.white().bold());
104 println!(
105 " {} {}",
106 "PID:".bright_black(),
107 proc.pid.to_string().cyan()
108 );
109
110 if let Some(ref cwd) = proc.cwd {
111 println!(" {} {}", "Directory:".bright_black(), cwd);
112 }
113
114 if let Some(ref path) = proc.exe_path {
115 println!(" {} {}", "Path:".bright_black(), path);
116 }
117
118 if let Some(ref user) = proc.user {
119 println!(" {} {}", "User:".bright_black(), user);
120 }
121
122 if let Some(ppid) = proc.parent_pid {
123 println!(
124 " {} {}",
125 "Parent PID:".bright_black(),
126 ppid.to_string().cyan()
127 );
128 }
129
130 let status_str = format!("{:?}", proc.status);
131 let status_colored = colorize_status(&proc.status, &status_str);
132 println!(" {} {}", "Status:".bright_black(), status_colored);
133
134 println!(" {} {:.1}%", "CPU:".bright_black(), proc.cpu_percent);
135 println!(
136 " {} {}",
137 "Memory:".bright_black(),
138 format_memory(proc.memory_mb)
139 );
140
141 if let Some(start_time) = proc.start_time {
142 let duration = std::time::SystemTime::now()
143 .duration_since(std::time::UNIX_EPOCH)
144 .map(|d| d.as_secs().saturating_sub(start_time))
145 .unwrap_or(0);
146
147 let uptime = format_duration(duration);
148 println!(" {} {}", "Uptime:".bright_black(), uptime);
149 }
150
151 if self.verbose {
152 if let Some(ref cmd) = proc.command {
153 println!(" {} {}", "Command:".bright_black(), cmd.bright_black());
154 }
155 }
156
157 println!();
158 }
159}
160
161#[derive(Serialize)]
162struct InfoOutput<'a> {
163 action: &'static str,
164 success: bool,
165 found_count: usize,
166 not_found_count: usize,
167 processes: &'a [Process],
168 not_found: &'a [String],
169}