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