proc_cli/commands/
list.rs1use crate::core::{Process, ProcessStatus};
11use crate::error::Result;
12use crate::ui::{OutputFormat, Printer};
13use clap::Args;
14use std::path::PathBuf;
15
16#[derive(Args, Debug)]
18pub struct ListCommand {
19 pub name: Option<String>,
21
22 #[arg(long = "in", short = 'i', num_args = 0..=1, default_missing_value = ".")]
24 pub in_dir: Option<String>,
25
26 #[arg(long, short = 'p')]
28 pub path: Option<String>,
29
30 #[arg(long)]
32 pub min_cpu: Option<f32>,
33
34 #[arg(long)]
36 pub min_mem: Option<f64>,
37
38 #[arg(long)]
40 pub status: Option<String>,
41
42 #[arg(long, short = 'j')]
44 pub json: bool,
45
46 #[arg(long, short = 'v')]
48 pub verbose: bool,
49
50 #[arg(long, short = 'n')]
52 pub limit: Option<usize>,
53
54 #[arg(long, short = 's', default_value = "cpu")]
56 pub sort: String,
57}
58
59impl ListCommand {
60 pub fn execute(&self) -> Result<()> {
62 let format = if self.json {
63 OutputFormat::Json
64 } else {
65 OutputFormat::Human
66 };
67 let printer = Printer::new(format, self.verbose);
68
69 let mut processes = if let Some(ref name) = self.name {
71 Process::find_by_name(name)?
72 } else {
73 Process::find_all()?
74 };
75
76 let in_dir_filter: Option<PathBuf> = self.in_dir.as_ref().map(|p| {
78 if p == "." {
79 std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
80 } else {
81 let path = PathBuf::from(p);
82 if path.is_relative() {
83 std::env::current_dir()
84 .unwrap_or_else(|_| PathBuf::from("."))
85 .join(path)
86 } else {
87 path
88 }
89 }
90 });
91
92 let path_filter: Option<PathBuf> = self.path.as_ref().map(|p| {
94 let path = PathBuf::from(p);
95 if path.is_relative() {
96 std::env::current_dir()
97 .unwrap_or_else(|_| PathBuf::from("."))
98 .join(path)
99 } else {
100 path
101 }
102 });
103
104 processes.retain(|p| {
106 if let Some(ref dir_path) = in_dir_filter {
108 if let Some(ref proc_cwd) = p.cwd {
109 let proc_path = PathBuf::from(proc_cwd);
110 if !proc_path.starts_with(dir_path) {
111 return false;
112 }
113 } else {
114 return false;
115 }
116 }
117
118 if let Some(ref exe_path) = path_filter {
120 if let Some(ref proc_exe) = p.exe_path {
121 let proc_path = PathBuf::from(proc_exe);
122 if !proc_path.starts_with(exe_path) {
123 return false;
124 }
125 } else {
126 return false;
127 }
128 }
129
130 if let Some(min_cpu) = self.min_cpu {
132 if p.cpu_percent < min_cpu {
133 return false;
134 }
135 }
136
137 if let Some(min_mem) = self.min_mem {
139 if p.memory_mb < min_mem {
140 return false;
141 }
142 }
143
144 if let Some(ref status) = self.status {
146 let status_match = match status.to_lowercase().as_str() {
147 "running" => matches!(p.status, ProcessStatus::Running),
148 "sleeping" | "sleep" => matches!(p.status, ProcessStatus::Sleeping),
149 "stopped" | "stop" => matches!(p.status, ProcessStatus::Stopped),
150 "zombie" => matches!(p.status, ProcessStatus::Zombie),
151 _ => true,
152 };
153 if !status_match {
154 return false;
155 }
156 }
157
158 true
159 });
160
161 match self.sort.to_lowercase().as_str() {
163 "cpu" => processes.sort_by(|a, b| {
164 b.cpu_percent
165 .partial_cmp(&a.cpu_percent)
166 .unwrap_or(std::cmp::Ordering::Equal)
167 }),
168 "mem" | "memory" => processes.sort_by(|a, b| {
169 b.memory_mb
170 .partial_cmp(&a.memory_mb)
171 .unwrap_or(std::cmp::Ordering::Equal)
172 }),
173 "pid" => processes.sort_by_key(|p| p.pid),
174 "name" => processes.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase())),
175 _ => {} }
177
178 if let Some(limit) = self.limit {
180 processes.truncate(limit);
181 }
182
183 let context = in_dir_filter
185 .as_ref()
186 .map(|p| format!("in {}", p.display()));
187
188 printer.print_processes_with_context(&processes, context.as_deref());
189 Ok(())
190 }
191}