proc_cli/commands/
info.rs1use crate::core::{apply_filters, parse_targets, resolve_target, Process};
11use crate::error::{ExitCode, ProcError, 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 if found.is_empty() {
94 return Err(ProcError::reported(
95 ExitCode::NotFound,
96 format!("No process found matching '{}'", self.targets.join(" ")),
97 ));
98 }
99
100 Ok(())
101 }
102
103 fn print_process_info(&self, proc: &Process) {
104 println!(
105 "{} Process {}",
106 "✓".green().bold(),
107 proc.pid.to_string().cyan().bold()
108 );
109 println!();
110 println!(" {} {}", "Name:".bright_black(), proc.name.white().bold());
111 println!(
112 " {} {}",
113 "PID:".bright_black(),
114 proc.pid.to_string().cyan()
115 );
116
117 if let Some(ref cwd) = proc.cwd {
118 println!(" {} {}", "Directory:".bright_black(), cwd);
119 }
120
121 if let Some(ref path) = proc.exe_path {
122 println!(" {} {}", "Path:".bright_black(), path);
123 }
124
125 if let Some(ref user) = proc.user {
126 println!(" {} {}", "User:".bright_black(), user);
127 }
128
129 if let Some(ppid) = proc.parent_pid {
130 println!(
131 " {} {}",
132 "Parent PID:".bright_black(),
133 ppid.to_string().cyan()
134 );
135 }
136
137 let status_str = format!("{:?}", proc.status);
138 let status_colored = colorize_status(&proc.status, &status_str);
139 println!(" {} {}", "Status:".bright_black(), status_colored);
140
141 println!(" {} {:.1}%", "CPU:".bright_black(), proc.cpu_percent);
142 println!(
143 " {} {}",
144 "Memory:".bright_black(),
145 format_memory(proc.memory_mb)
146 );
147
148 if let Some(start_time) = proc.start_time {
149 let duration = std::time::SystemTime::now()
150 .duration_since(std::time::UNIX_EPOCH)
151 .map(|d| d.as_secs().saturating_sub(start_time))
152 .unwrap_or(0);
153
154 let uptime = format_duration(duration);
155 println!(" {} {}", "Uptime:".bright_black(), uptime);
156 }
157
158 if self.verbose {
159 if let Some(ref cmd) = proc.command {
160 println!(" {} {}", "Command:".bright_black(), cmd.bright_black());
161 }
162 }
163
164 println!();
165 }
166}
167
168#[derive(Serialize)]
169struct InfoOutput<'a> {
170 action: &'static str,
171 success: bool,
172 found_count: usize,
173 not_found_count: usize,
174 processes: &'a [Process],
175 not_found: &'a [String],
176}